DO NOT MERGE: Merge Oreo MR1 into master
Exempt-From-Owner-Approval: Changes already landed internally
Change-Id: I737a553e30ee52871bdf0668cf44b54d50fa73dc
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 4bf7e9a..d828faa 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -54,6 +54,9 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android.car7_intermediates/)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android.car_intermediates/)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android.car_intermediates/src/android/car/hardware/ICarDiagnostic*.java)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android.car7_intermediates/src/android/car/hardware/ICarDiagnostic*.java)
+
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
diff --git a/TrustAgent/Android.mk b/TrustAgent/Android.mk
index dc70ef3..a57308a 100644
--- a/TrustAgent/Android.mk
+++ b/TrustAgent/Android.mk
@@ -13,6 +13,8 @@
# limitations under the License.
#
+ifneq ($(TARGET_BUILD_PDK), true)
+
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
@@ -37,3 +39,4 @@
include $(BUILD_PACKAGE)
+endif
diff --git a/TrustAgent/src/com/android/car/trust/CarBleTrustAgent.java b/TrustAgent/src/com/android/car/trust/CarBleTrustAgent.java
index 99bbf22..997f028 100644
--- a/TrustAgent/src/com/android/car/trust/CarBleTrustAgent.java
+++ b/TrustAgent/src/com/android/car/trust/CarBleTrustAgent.java
@@ -41,7 +41,9 @@
* A sample trust agent that demonstrates how to use the escrow token unlock APIs. </p>
*
* This trust agent runs during direct boot and binds to a BLE service that listens for remote
- * devices to trigger an unlock.
+ * devices to trigger an unlock. <p/>
+ *
+ * The permissions for this agent must be enabled as priv-app permissions for it to start.
*/
public class CarBleTrustAgent extends TrustAgentService {
public static final String ACTION_REVOKE_TRUST = "revoke-trust-action";
diff --git a/TrustAgent/src/com/android/car/trust/PhoneEnrolmentActivity.java b/TrustAgent/src/com/android/car/trust/PhoneEnrolmentActivity.java
index dac4b79..6e187e2 100644
--- a/TrustAgent/src/com/android/car/trust/PhoneEnrolmentActivity.java
+++ b/TrustAgent/src/com/android/car/trust/PhoneEnrolmentActivity.java
@@ -21,7 +21,13 @@
import android.widget.TextView;
/**
- * Activity to allow the user to add an escrow token to a remote device.
+ * Activity to allow the user to add an escrow token to a remote device. <p/>
+ *
+ * For this to work properly, the correct permissions must be set in the system config. In AOSP,
+ * this config is in frameworks/base/core/res/res/values/config.xml <p/>
+ *
+ * The config must set config_allowEscrowTokenForTrustAgent to true. For the desired car
+ * experience, the config should also set config_strongAuthRequiredOnBoot to false.
*/
public class PhoneEnrolmentActivity extends Activity {
private Button mScanButton;
diff --git a/car-cluster-logging-renderer/Android.mk b/car-cluster-logging-renderer/Android.mk
index 8fb7674..dd1d0ca 100644
--- a/car-cluster-logging-renderer/Android.mk
+++ b/car-cluster-logging-renderer/Android.mk
@@ -13,7 +13,7 @@
# limitations under the License.
#
#
-
+ifneq ($(TARGET_BUILD_PDK),true)
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
@@ -32,3 +32,4 @@
LOCAL_JAVA_LIBRARIES += android.car
include $(BUILD_PACKAGE)
+endif
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/api/2.txt b/car-lib/api/2.txt
new file mode 100644
index 0000000..fb94b19
--- /dev/null
+++ b/car-lib/api/2.txt
@@ -0,0 +1,297 @@
+package android.car {
+
+ public final class Car {
+ method public void connect() throws java.lang.IllegalStateException;
+ method public static android.car.Car createCar(android.content.Context, android.content.ServiceConnection, android.os.Handler);
+ method public static android.car.Car createCar(android.content.Context, android.content.ServiceConnection);
+ method public void disconnect();
+ method public int getCarConnectionType();
+ method public java.lang.Object getCarManager(java.lang.String) throws android.car.CarNotConnectedException;
+ method public boolean isConnected();
+ method public boolean isConnecting();
+ field public static final java.lang.String APP_FOCUS_SERVICE = "app_focus";
+ field public static final java.lang.String AUDIO_SERVICE = "audio";
+ field public static final int CONNECTION_TYPE_EMBEDDED = 5; // 0x5
+ field public static final java.lang.String INFO_SERVICE = "info";
+ field public static final java.lang.String PACKAGE_SERVICE = "package";
+ field public static final java.lang.String PERMISSION_CAR_CONTROL_AUDIO_VOLUME = "android.car.permission.CAR_CONTROL_AUDIO_VOLUME";
+ field public static final java.lang.String PERMISSION_FUEL = "android.car.permission.CAR_FUEL";
+ field public static final java.lang.String PERMISSION_MILEAGE = "android.car.permission.CAR_MILEAGE";
+ field public static final java.lang.String PERMISSION_SPEED = "android.car.permission.CAR_SPEED";
+ field public static final java.lang.String SENSOR_SERVICE = "sensor";
+ field public static final int VERSION = 2; // 0x2
+ }
+
+ public final class CarAppFocusManager {
+ method public void abandonAppFocus(android.car.CarAppFocusManager.OnAppFocusOwnershipCallback, int);
+ method public void abandonAppFocus(android.car.CarAppFocusManager.OnAppFocusOwnershipCallback);
+ method public void addFocusListener(android.car.CarAppFocusManager.OnAppFocusChangedListener, int) throws android.car.CarNotConnectedException;
+ method public boolean isOwningFocus(android.car.CarAppFocusManager.OnAppFocusOwnershipCallback, int) throws android.car.CarNotConnectedException;
+ method public void removeFocusListener(android.car.CarAppFocusManager.OnAppFocusChangedListener, int);
+ method public void removeFocusListener(android.car.CarAppFocusManager.OnAppFocusChangedListener);
+ method public int requestAppFocus(int, android.car.CarAppFocusManager.OnAppFocusOwnershipCallback) throws android.car.CarNotConnectedException, java.lang.SecurityException;
+ field public static final int APP_FOCUS_REQUEST_FAILED = 0; // 0x0
+ field public static final int APP_FOCUS_REQUEST_SUCCEEDED = 1; // 0x1
+ field public static final int APP_FOCUS_TYPE_NAVIGATION = 1; // 0x1
+ field public static final int APP_FOCUS_TYPE_VOICE_COMMAND = 2; // 0x2
+ }
+
+ public static abstract interface CarAppFocusManager.OnAppFocusChangedListener {
+ method public abstract void onAppFocusChanged(int, boolean);
+ }
+
+ public static abstract interface CarAppFocusManager.OnAppFocusOwnershipCallback {
+ method public abstract void onAppFocusOwnershipGranted(int);
+ method public abstract void onAppFocusOwnershipLost(int);
+ }
+
+ public final class CarInfoManager {
+ method public java.lang.String getManufacturer() throws android.car.CarNotConnectedException;
+ method public java.lang.String getModel() throws android.car.CarNotConnectedException;
+ method public java.lang.String getModelYear() throws android.car.CarNotConnectedException;
+ method public java.lang.String getVehicleId() throws android.car.CarNotConnectedException;
+ }
+
+ public class CarNotConnectedException extends java.lang.Exception {
+ ctor public CarNotConnectedException();
+ ctor public CarNotConnectedException(java.lang.String);
+ ctor public CarNotConnectedException(java.lang.String, java.lang.Throwable);
+ ctor public CarNotConnectedException(java.lang.Exception);
+ }
+
+}
+
+package android.car.app.menu {
+
+ public abstract class CarMenuCallbacks {
+ ctor public CarMenuCallbacks();
+ method public abstract android.car.app.menu.RootMenu getRootMenu(android.os.Bundle);
+ method public abstract void onCarMenuClosed();
+ method public abstract void onCarMenuClosing();
+ method public abstract void onCarMenuOpened();
+ method public abstract void onCarMenuOpening();
+ method public abstract void onItemClicked(java.lang.String);
+ method public abstract boolean onItemLongClicked(java.lang.String);
+ method public abstract boolean onMenuClicked();
+ method public abstract void subscribe(java.lang.String, android.car.app.menu.SubscriptionCallbacks);
+ method public abstract void unsubscribe(java.lang.String, android.car.app.menu.SubscriptionCallbacks);
+ }
+
+ public class CarMenuConstants {
+ ctor public CarMenuConstants();
+ }
+
+ public static class CarMenuConstants.MenuItemConstants {
+ ctor public CarMenuConstants.MenuItemConstants();
+ field public static final int FLAG_BROWSABLE = 1; // 0x1
+ field public static final int FLAG_FIRSTITEM = 2; // 0x2
+ field public static final java.lang.String KEY_EMPTY_PLACEHOLDER = "android.car.app.menu.empty_placeholder";
+ field public static final java.lang.String KEY_FLAGS = "android.car.app.menu.flags";
+ field public static final java.lang.String KEY_ID = "android.car.app.menu.id";
+ field public static final java.lang.String KEY_LEFTICON = "android.car.app.menu.leftIcon";
+ field public static final java.lang.String KEY_REMOTEVIEWS = "android.car.app.menu.remoteViews";
+ field public static final java.lang.String KEY_RIGHTICON = "android.car.app.menu.rightIcon";
+ field public static final java.lang.String KEY_RIGHTTEXT = "android.car.app.menu.rightText";
+ field public static final java.lang.String KEY_TEXT = "android.car.app.menu.text";
+ field public static final java.lang.String KEY_TITLE = "android.car.app.menu.title";
+ field public static final java.lang.String KEY_WIDGET = "android.car.app.menu.widget";
+ field public static final java.lang.String KEY_WIDGET_STATE = "android.car.app.menu.widget_state";
+ field public static final int WIDGET_CHECKBOX = 1; // 0x1
+ field public static final int WIDGET_TEXT_VIEW = 2; // 0x2
+ }
+
+ public static abstract class CarMenuConstants.MenuItemConstants.MenuItemFlags implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class CarMenuConstants.MenuItemConstants.WidgetTypes implements java.lang.annotation.Annotation {
+ }
+
+ public abstract class CarUiEntry {
+ ctor public CarUiEntry(android.content.Context, android.content.Context);
+ method public abstract void closeDrawer();
+ method public abstract android.view.View getContentView();
+ method public abstract int getFragmentContainerId();
+ method public abstract java.lang.CharSequence getSearchBoxText();
+ method public abstract void hideMenuButton();
+ method public abstract void hideTitle();
+ method public abstract void onPause();
+ method public abstract void onRestoreInstanceState(android.os.Bundle);
+ method public abstract void onResume();
+ method public abstract void onSaveInstanceState(android.os.Bundle);
+ method public abstract void onStart();
+ method public abstract void onStop();
+ method public abstract void openDrawer();
+ method public abstract void restoreMenuDrawable();
+ method public abstract void setAutoLightDarkMode();
+ method public abstract void setBackground(android.graphics.Bitmap);
+ method public abstract void setCarMenuCallbacks(android.car.app.menu.CarMenuCallbacks);
+ method public abstract void setDarkMode();
+ method public abstract void setLightMode();
+ method public abstract void setMenuButtonBitmap(android.graphics.Bitmap);
+ method public abstract void setMenuButtonColor(int);
+ method public abstract void setScrimColor(int);
+ method public abstract void setSearchBoxColors(int, int, int, int);
+ method public abstract void setSearchBoxEditListener(android.car.app.menu.SearchBoxEditListener);
+ method public abstract void setSearchBoxEndView(android.view.View);
+ method public abstract void setTitle(java.lang.CharSequence);
+ method public abstract void showMenu(java.lang.String, java.lang.String);
+ method public abstract void showSearchBox(android.view.View.OnClickListener);
+ method public abstract void showTitle();
+ method public abstract void showToast(java.lang.String, long);
+ method public abstract android.widget.EditText startInput(java.lang.String, android.view.View.OnClickListener);
+ method public abstract void stopInput();
+ field protected final android.content.Context mAppContext;
+ field protected final android.content.Context mUiLibContext;
+ }
+
+ public class RootMenu {
+ ctor public RootMenu(java.lang.String);
+ ctor public RootMenu(java.lang.String, android.os.Bundle);
+ method public android.os.Bundle getBundle();
+ method public java.lang.String getId();
+ }
+
+ public abstract class SearchBoxEditListener {
+ ctor public SearchBoxEditListener();
+ method public abstract void onEdit(java.lang.String);
+ method public abstract void onSearch(java.lang.String);
+ }
+
+ public abstract class SubscriptionCallbacks {
+ ctor public SubscriptionCallbacks();
+ method public abstract void onChildChanged(java.lang.String, android.os.Bundle);
+ method public abstract void onChildrenLoaded(java.lang.String, java.util.List<android.os.Bundle>);
+ method public abstract void onError(java.lang.String);
+ }
+
+}
+
+package android.car.content.pm {
+
+ public final class CarPackageManager {
+ method public boolean isActivityAllowedWhileDriving(java.lang.String, java.lang.String) throws android.car.CarNotConnectedException;
+ method public boolean isServiceAllowedWhileDriving(java.lang.String, java.lang.String) throws android.car.CarNotConnectedException;
+ }
+
+}
+
+package android.car.hardware {
+
+ public class CarSensorEvent implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.car.hardware.CarSensorEvent> CREATOR;
+ field public static final int DRIVE_STATUS_FULLY_RESTRICTED = 31; // 0x1f
+ field public static final int DRIVE_STATUS_LIMIT_MESSAGE_LEN = 16; // 0x10
+ field public static final int DRIVE_STATUS_NO_CONFIG = 8; // 0x8
+ field public static final int DRIVE_STATUS_NO_KEYBOARD_INPUT = 2; // 0x2
+ field public static final int DRIVE_STATUS_NO_VIDEO = 1; // 0x1
+ field public static final int DRIVE_STATUS_NO_VOICE_INPUT = 4; // 0x4
+ field public static final int DRIVE_STATUS_UNRESTRICTED = 0; // 0x0
+ field public static final int GEAR_DRIVE = 100; // 0x64
+ field public static final int GEAR_EIGHTH = 8; // 0x8
+ field public static final int GEAR_FIFTH = 5; // 0x5
+ field public static final int GEAR_FIRST = 1; // 0x1
+ field public static final int GEAR_FOURTH = 4; // 0x4
+ field public static final int GEAR_NEUTRAL = 0; // 0x0
+ field public static final int GEAR_NINTH = 9; // 0x9
+ field public static final int GEAR_PARK = 101; // 0x65
+ field public static final int GEAR_REVERSE = 102; // 0x66
+ field public static final int GEAR_SECOND = 2; // 0x2
+ field public static final int GEAR_SEVENTH = 7; // 0x7
+ field public static final int GEAR_SIXTH = 6; // 0x6
+ field public static final int GEAR_TENTH = 10; // 0xa
+ field public static final int GEAR_THIRD = 3; // 0x3
+ field public static final int IGNITION_STATE_ACC = 3; // 0x3
+ field public static final int IGNITION_STATE_LOCK = 1; // 0x1
+ field public static final int IGNITION_STATE_OFF = 2; // 0x2
+ field public static final int IGNITION_STATE_ON = 4; // 0x4
+ field public static final int IGNITION_STATE_START = 5; // 0x5
+ field public static final int IGNITION_STATE_UNDEFINED = 0; // 0x0
+ field public static final int INDEX_ENVIRONMENT_PRESSURE = 1; // 0x1
+ field public static final int INDEX_ENVIRONMENT_TEMPERATURE = 0; // 0x0
+ field public static final int INDEX_FUEL_LEVEL_IN_DISTANCE = 1; // 0x1
+ field public static final int INDEX_FUEL_LEVEL_IN_PERCENTILE = 0; // 0x0
+ field public static final int INDEX_FUEL_LOW_WARNING = 0; // 0x0
+ field public final float[] floatValues;
+ field public final int[] intValues;
+ field public int sensorType;
+ field public long timestamp;
+ }
+
+ public static class CarSensorEvent.EnvironmentData {
+ field public float pressure;
+ field public float temperature;
+ field public long timestamp;
+ }
+
+ public final class CarSensorManager {
+ method public android.car.hardware.CarSensorEvent getLatestSensorEvent(int) throws android.car.CarNotConnectedException;
+ method public int[] getSupportedSensors() throws android.car.CarNotConnectedException;
+ method public boolean isSensorSupported(int) throws android.car.CarNotConnectedException;
+ method public static boolean isSensorSupported(int[], int);
+ method public boolean registerListener(android.car.hardware.CarSensorManager.OnSensorChangedListener, int, int) throws android.car.CarNotConnectedException, java.lang.IllegalArgumentException;
+ method public void unregisterListener(android.car.hardware.CarSensorManager.OnSensorChangedListener);
+ method public void unregisterListener(android.car.hardware.CarSensorManager.OnSensorChangedListener, int);
+ field public static final int SENSOR_RATE_FAST = 1; // 0x1
+ field public static final int SENSOR_RATE_FASTEST = 0; // 0x0
+ field public static final int SENSOR_RATE_NORMAL = 3; // 0x3
+ field public static final int SENSOR_RATE_UI = 2; // 0x2
+ field public static final int SENSOR_TYPE_CAR_SPEED = 2; // 0x2
+ field public static final int SENSOR_TYPE_DRIVING_STATUS = 11; // 0xb
+ field public static final int SENSOR_TYPE_ENVIRONMENT = 12; // 0xc
+ field public static final int SENSOR_TYPE_FUEL_LEVEL = 5; // 0x5
+ field public static final int SENSOR_TYPE_GEAR = 7; // 0x7
+ field public static final int SENSOR_TYPE_IGNITION_STATE = 22; // 0x16
+ field public static final int SENSOR_TYPE_NIGHT = 9; // 0x9
+ field public static final int SENSOR_TYPE_ODOMETER = 4; // 0x4
+ field public static final int SENSOR_TYPE_PARKING_BRAKE = 6; // 0x6
+ field public static final int SENSOR_TYPE_RPM = 3; // 0x3
+ field public static final int SENSOR_TYPE_VENDOR_EXTENSION_END = 1879048191; // 0x6fffffff
+ }
+
+ public static abstract interface CarSensorManager.OnSensorChangedListener {
+ method public abstract void onSensorChanged(android.car.hardware.CarSensorEvent);
+ }
+
+}
+
+package android.car.media {
+
+ public final class CarAudioManager {
+ method public void abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
+ method public android.media.AudioAttributes getAudioAttributesForCarUsage(int) throws android.car.CarNotConnectedException;
+ method public int getStreamMaxVolume(int) throws android.car.CarNotConnectedException;
+ method public int getStreamMinVolume(int) throws android.car.CarNotConnectedException;
+ method public int getStreamVolume(int) throws android.car.CarNotConnectedException;
+ method public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int) throws android.car.CarNotConnectedException, java.lang.IllegalArgumentException;
+ method public void setStreamVolume(int, int, int) throws android.car.CarNotConnectedException;
+ field public static final int CAR_AUDIO_USAGE_ALARM = 6; // 0x6
+ field public static final int CAR_AUDIO_USAGE_DEFAULT = 0; // 0x0
+ field public static final int CAR_AUDIO_USAGE_MUSIC = 1; // 0x1
+ field public static final int CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE = 3; // 0x3
+ field public static final int CAR_AUDIO_USAGE_NOTIFICATION = 7; // 0x7
+ field public static final int CAR_AUDIO_USAGE_RADIO = 2; // 0x2
+ field public static final int CAR_AUDIO_USAGE_SYSTEM_SAFETY_ALERT = 9; // 0x9
+ field public static final int CAR_AUDIO_USAGE_SYSTEM_SOUND = 8; // 0x8
+ field public static final int CAR_AUDIO_USAGE_VOICE_CALL = 4; // 0x4
+ field public static final int CAR_AUDIO_USAGE_VOICE_COMMAND = 5; // 0x5
+ }
+
+}
+
+package android.car.settings {
+
+ public class CarSettings {
+ ctor public CarSettings();
+ }
+
+ public static final class CarSettings.Global {
+ ctor public CarSettings.Global();
+ field public static final java.lang.String KEY_GARAGE_MODE_ENABLED = "android.car.GARAGE_MODE_ENABLED";
+ field public static final java.lang.String KEY_GARAGE_MODE_MAINTENANCE_WINDOW = "android.car.GARAGE_MODE_MAINTENANCE_WINDOW";
+ field public static final java.lang.String KEY_GARAGE_MODE_WAKE_UP_TIME = "android.car.GARAGE_MODE_WAKE_UP_TIME";
+ }
+
+}
+
diff --git a/car-lib/api/current.txt b/car-lib/api/current.txt
index fb94b19..242c337 100644
--- a/car-lib/api/current.txt
+++ b/car-lib/api/current.txt
@@ -11,6 +11,8 @@
method public boolean isConnecting();
field public static final java.lang.String APP_FOCUS_SERVICE = "app_focus";
field public static final java.lang.String AUDIO_SERVICE = "audio";
+ field public static final java.lang.String CAR_EXTRA_MEDIA_PACKAGE = "android.car.intent.extra.MEDIA_PACKAGE";
+ field public static final java.lang.String CAR_INTENT_ACTION_MEDIA_TEMPLATE = "android.car.intent.action.MEDIA_TEMPLATE";
field public static final int CONNECTION_TYPE_EMBEDDED = 5; // 0x5
field public static final java.lang.String INFO_SERVICE = "info";
field public static final java.lang.String PACKAGE_SERVICE = "package";
@@ -18,8 +20,9 @@
field public static final java.lang.String PERMISSION_FUEL = "android.car.permission.CAR_FUEL";
field public static final java.lang.String PERMISSION_MILEAGE = "android.car.permission.CAR_MILEAGE";
field public static final java.lang.String PERMISSION_SPEED = "android.car.permission.CAR_SPEED";
+ field public static final java.lang.String PERMISSION_VEHICLE_DYNAMICS_STATE = "android.car.permission.VEHICLE_DYNAMICS_STATE";
field public static final java.lang.String SENSOR_SERVICE = "sensor";
- field public static final int VERSION = 2; // 0x2
+ field public static final int VERSION = 3; // 0x3
}
public final class CarAppFocusManager {
@@ -213,8 +216,14 @@
field public static final int INDEX_FUEL_LEVEL_IN_DISTANCE = 1; // 0x1
field public static final int INDEX_FUEL_LEVEL_IN_PERCENTILE = 0; // 0x0
field public static final int INDEX_FUEL_LOW_WARNING = 0; // 0x0
+ field public static final int INDEX_WHEEL_DISTANCE_FRONT_LEFT = 1; // 0x1
+ field public static final int INDEX_WHEEL_DISTANCE_FRONT_RIGHT = 2; // 0x2
+ field public static final int INDEX_WHEEL_DISTANCE_REAR_LEFT = 4; // 0x4
+ field public static final int INDEX_WHEEL_DISTANCE_REAR_RIGHT = 3; // 0x3
+ field public static final int INDEX_WHEEL_DISTANCE_RESET_COUNT = 0; // 0x0
field public final float[] floatValues;
field public final int[] intValues;
+ field public final long[] longValues;
field public int sensorType;
field public long timestamp;
}
@@ -237,6 +246,7 @@
field public static final int SENSOR_RATE_FASTEST = 0; // 0x0
field public static final int SENSOR_RATE_NORMAL = 3; // 0x3
field public static final int SENSOR_RATE_UI = 2; // 0x2
+ field public static final int SENSOR_TYPE_ABS_ACTIVE = 24; // 0x18
field public static final int SENSOR_TYPE_CAR_SPEED = 2; // 0x2
field public static final int SENSOR_TYPE_DRIVING_STATUS = 11; // 0xb
field public static final int SENSOR_TYPE_ENVIRONMENT = 12; // 0xc
@@ -247,7 +257,9 @@
field public static final int SENSOR_TYPE_ODOMETER = 4; // 0x4
field public static final int SENSOR_TYPE_PARKING_BRAKE = 6; // 0x6
field public static final int SENSOR_TYPE_RPM = 3; // 0x3
+ field public static final int SENSOR_TYPE_TRACTION_CONTROL_ACTIVE = 25; // 0x19
field public static final int SENSOR_TYPE_VENDOR_EXTENSION_END = 1879048191; // 0x6fffffff
+ field public static final int SENSOR_TYPE_WHEEL_TICK_DISTANCE = 23; // 0x17
}
public static abstract interface CarSensorManager.OnSensorChangedListener {
@@ -272,6 +284,7 @@
field public static final int CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE = 3; // 0x3
field public static final int CAR_AUDIO_USAGE_NOTIFICATION = 7; // 0x7
field public static final int CAR_AUDIO_USAGE_RADIO = 2; // 0x2
+ field public static final int CAR_AUDIO_USAGE_RINGTONE = 10; // 0xa
field public static final int CAR_AUDIO_USAGE_SYSTEM_SAFETY_ALERT = 9; // 0x9
field public static final int CAR_AUDIO_USAGE_SYSTEM_SOUND = 8; // 0x8
field public static final int CAR_AUDIO_USAGE_VOICE_CALL = 4; // 0x4
diff --git a/car-lib/api/system-1.txt b/car-lib/api/system-1.txt
index 763114b..253c874 100644
--- a/car-lib/api/system-1.txt
+++ b/car-lib/api/system-1.txt
@@ -77,9 +77,9 @@
public final class CarProjectionManager {
method public void onCarDisconnected();
method public void registerProjectionRunner(android.content.Intent) throws android.car.CarNotConnectedException;
- method public void regsiterProjectionListener(android.car.CarProjectionManager.CarProjectionListener, int) throws android.car.CarNotConnectedException;
+ method public void registerProjectionListener(android.car.CarProjectionManager.CarProjectionListener, int) throws android.car.CarNotConnectedException;
method public void unregisterProjectionRunner(android.content.Intent);
- method public void unregsiterProjectionListener();
+ method public void unregisterProjectionListener();
field public static final int PROJECTION_LONG_PRESS_VOICE_SEARCH = 2; // 0x2
field public static final int PROJECTION_VOICE_SEARCH = 1; // 0x1
}
diff --git a/car-lib/api/system-2.txt b/car-lib/api/system-2.txt
new file mode 100644
index 0000000..e8540ad
--- /dev/null
+++ b/car-lib/api/system-2.txt
@@ -0,0 +1,752 @@
+package android.car {
+
+ public final class Car {
+ method public void connect() throws java.lang.IllegalStateException;
+ method public static android.car.Car createCar(android.content.Context, android.content.ServiceConnection, android.os.Handler);
+ method public static android.car.Car createCar(android.content.Context, android.content.ServiceConnection);
+ method public void disconnect();
+ method public int getCarConnectionType();
+ method public java.lang.Object getCarManager(java.lang.String) throws android.car.CarNotConnectedException;
+ method public boolean isConnected();
+ method public boolean isConnecting();
+ field public static final java.lang.String APP_FOCUS_SERVICE = "app_focus";
+ field public static final java.lang.String AUDIO_SERVICE = "audio";
+ field public static final java.lang.String CABIN_SERVICE = "cabin";
+ field public static final int CONNECTION_TYPE_EMBEDDED = 5; // 0x5
+ field public static final java.lang.String DIAGNOSTIC_SERVICE = "diagnostic";
+ field public static final java.lang.String HVAC_SERVICE = "hvac";
+ field public static final java.lang.String INFO_SERVICE = "info";
+ field public static final java.lang.String PACKAGE_SERVICE = "package";
+ field public static final java.lang.String PERMISSION_CAR_CABIN = "android.car.permission.CAR_CABIN";
+ field public static final java.lang.String PERMISSION_CAR_CONTROL_AUDIO_VOLUME = "android.car.permission.CAR_CONTROL_AUDIO_VOLUME";
+ field public static final java.lang.String PERMISSION_CAR_DIAGNOSTIC_CLEAR = "android.car.permission.DIAGNOSTIC_CLEAR";
+ field public static final java.lang.String PERMISSION_CAR_DIAGNOSTIC_READ = "android.car.permission.DIAGNOSTIC_READ";
+ field public static final java.lang.String PERMISSION_CAR_HVAC = "android.car.permission.CAR_HVAC";
+ field public static final java.lang.String PERMISSION_CAR_PROJECTION = "android.car.permission.CAR_PROJECTION";
+ field public static final java.lang.String PERMISSION_CAR_RADIO = "android.car.permission.CAR_RADIO";
+ field public static final java.lang.String PERMISSION_CAR_TEST_SERVICE = "android.car.permission.CAR_TEST_SERVICE";
+ field public static final java.lang.String PERMISSION_CONTROL_APP_BLOCKING = "android.car.permission.CONTROL_APP_BLOCKING";
+ field public static final java.lang.String PERMISSION_FUEL = "android.car.permission.CAR_FUEL";
+ field public static final java.lang.String PERMISSION_MILEAGE = "android.car.permission.CAR_MILEAGE";
+ field public static final deprecated java.lang.String PERMISSION_MOCK_VEHICLE_HAL = "android.car.permission.CAR_MOCK_VEHICLE_HAL";
+ field public static final java.lang.String PERMISSION_SPEED = "android.car.permission.CAR_SPEED";
+ field public static final java.lang.String PERMISSION_VENDOR_EXTENSION = "android.car.permission.CAR_VENDOR_EXTENSION";
+ field public static final java.lang.String PERMISSION_VMS_PUBLISHER = "android.car.permission.VMS_PUBLISHER";
+ field public static final java.lang.String PERMISSION_VMS_SUBSCRIBER = "android.car.permission.VMS_SUBSCRIBER";
+ field public static final java.lang.String PROJECTION_SERVICE = "projection";
+ field public static final java.lang.String RADIO_SERVICE = "radio";
+ field public static final java.lang.String SENSOR_SERVICE = "sensor";
+ field public static final java.lang.String TEST_SERVICE = "car-service-test";
+ field public static final java.lang.String VENDOR_EXTENSION_SERVICE = "vendor_extension";
+ field public static final int VERSION = 2; // 0x2
+ field public static final java.lang.String VMS_SUBSCRIBER_SERVICE = "vehicle_map_subscriber_service";
+ }
+
+ public final class CarAppFocusManager {
+ method public void abandonAppFocus(android.car.CarAppFocusManager.OnAppFocusOwnershipCallback, int);
+ method public void abandonAppFocus(android.car.CarAppFocusManager.OnAppFocusOwnershipCallback);
+ method public void addFocusListener(android.car.CarAppFocusManager.OnAppFocusChangedListener, int) throws android.car.CarNotConnectedException;
+ method public boolean isOwningFocus(android.car.CarAppFocusManager.OnAppFocusOwnershipCallback, int) throws android.car.CarNotConnectedException;
+ method public void removeFocusListener(android.car.CarAppFocusManager.OnAppFocusChangedListener, int);
+ method public void removeFocusListener(android.car.CarAppFocusManager.OnAppFocusChangedListener);
+ method public int requestAppFocus(int, android.car.CarAppFocusManager.OnAppFocusOwnershipCallback) throws android.car.CarNotConnectedException, java.lang.SecurityException;
+ field public static final int APP_FOCUS_REQUEST_FAILED = 0; // 0x0
+ field public static final int APP_FOCUS_REQUEST_SUCCEEDED = 1; // 0x1
+ field public static final int APP_FOCUS_TYPE_NAVIGATION = 1; // 0x1
+ field public static final int APP_FOCUS_TYPE_VOICE_COMMAND = 2; // 0x2
+ }
+
+ public static abstract interface CarAppFocusManager.OnAppFocusChangedListener {
+ method public abstract void onAppFocusChanged(int, boolean);
+ }
+
+ public static abstract interface CarAppFocusManager.OnAppFocusOwnershipCallback {
+ method public abstract void onAppFocusOwnershipGranted(int);
+ method public abstract void onAppFocusOwnershipLost(int);
+ }
+
+ public final class CarInfoManager {
+ method public java.lang.String getManufacturer() throws android.car.CarNotConnectedException;
+ method public java.lang.String getModel() throws android.car.CarNotConnectedException;
+ method public java.lang.String getModelYear() throws android.car.CarNotConnectedException;
+ method public java.lang.String getVehicleId() throws android.car.CarNotConnectedException;
+ }
+
+ public class CarNotConnectedException extends java.lang.Exception {
+ ctor public CarNotConnectedException();
+ ctor public CarNotConnectedException(java.lang.String);
+ ctor public CarNotConnectedException(java.lang.String, java.lang.Throwable);
+ ctor public CarNotConnectedException(java.lang.Exception);
+ }
+
+ public final class CarProjectionManager {
+ method public void onCarDisconnected();
+ method public void registerProjectionRunner(android.content.Intent) throws android.car.CarNotConnectedException;
+ method public void regsiterProjectionListener(android.car.CarProjectionManager.CarProjectionListener, int) throws android.car.CarNotConnectedException;
+ method public void unregisterProjectionRunner(android.content.Intent);
+ method public void unregsiterProjectionListener();
+ field public static final int PROJECTION_LONG_PRESS_VOICE_SEARCH = 2; // 0x2
+ field public static final int PROJECTION_VOICE_SEARCH = 1; // 0x1
+ }
+
+ public static abstract interface CarProjectionManager.CarProjectionListener {
+ method public abstract void onVoiceAssistantRequest(boolean);
+ }
+
+ public final class VehicleAreaType {
+ field public static final int VEHICLE_AREA_TYPE_DOOR = 4; // 0x4
+ field public static final int VEHICLE_AREA_TYPE_MIRROR = 5; // 0x5
+ field public static final int VEHICLE_AREA_TYPE_NONE = 0; // 0x0
+ field public static final int VEHICLE_AREA_TYPE_SEAT = 3; // 0x3
+ field public static final int VEHICLE_AREA_TYPE_WINDOW = 2; // 0x2
+ field public static final int VEHICLE_AREA_TYPE_ZONE = 1; // 0x1
+ }
+
+ public final class VehicleDoor {
+ field public static final int DOOR_HOOD = 268435456; // 0x10000000
+ field public static final int DOOR_REAR = 536870912; // 0x20000000
+ field public static final int DOOR_ROW_1_LEFT = 1; // 0x1
+ field public static final int DOOR_ROW_1_RIGHT = 4; // 0x4
+ field public static final int DOOR_ROW_2_LEFT = 16; // 0x10
+ field public static final int DOOR_ROW_2_RIGHT = 64; // 0x40
+ field public static final int DOOR_ROW_3_LEFT = 256; // 0x100
+ field public static final int DOOR_ROW_3_RIGHT = 1024; // 0x400
+ }
+
+ public final class VehicleMirror {
+ field public static final int MIRROR_DRIVER_CENTER = 4; // 0x4
+ field public static final int MIRROR_DRIVER_LEFT = 1; // 0x1
+ field public static final int MIRROR_DRIVER_RIGHT = 2; // 0x2
+ }
+
+ public final class VehicleSeat {
+ field public static final int SEAT_ROW_1_CENTER = 2; // 0x2
+ field public static final int SEAT_ROW_1_LEFT = 1; // 0x1
+ field public static final int SEAT_ROW_1_RIGHT = 4; // 0x4
+ field public static final int SEAT_ROW_2_CENTER = 32; // 0x20
+ field public static final int SEAT_ROW_2_LEFT = 16; // 0x10
+ field public static final int SEAT_ROW_2_RIGHT = 64; // 0x40
+ field public static final int SEAT_ROW_3_CENTER = 512; // 0x200
+ field public static final int SEAT_ROW_3_LEFT = 256; // 0x100
+ field public static final int SEAT_ROW_3_RIGHT = 1024; // 0x400
+ }
+
+ public final class VehicleWindow {
+ field public static final int WINDOW_FRONT_WINDSHIELD = 1; // 0x1
+ field public static final int WINDOW_REAR_WINDSHIELD = 2; // 0x2
+ field public static final int WINDOW_ROOF_TOP = 4; // 0x4
+ field public static final int WINDOW_ROW_1_LEFT = 16; // 0x10
+ field public static final int WINDOW_ROW_1_RIGHT = 32; // 0x20
+ field public static final int WINDOW_ROW_2_LEFT = 256; // 0x100
+ field public static final int WINDOW_ROW_2_RIGHT = 512; // 0x200
+ field public static final int WINDOW_ROW_3_LEFT = 4096; // 0x1000
+ field public static final int WINDOW_ROW_3_RIGHT = 8192; // 0x2000
+ }
+
+ public final class VehicleZone {
+ field public static final int ZONE_ALL = -2147483648; // 0x80000000
+ field public static final int ZONE_ROW_1_ALL = 8; // 0x8
+ field public static final int ZONE_ROW_1_CENTER = 2; // 0x2
+ field public static final int ZONE_ROW_1_LEFT = 1; // 0x1
+ field public static final int ZONE_ROW_1_RIGHT = 4; // 0x4
+ field public static final int ZONE_ROW_2_ALL = 128; // 0x80
+ field public static final int ZONE_ROW_2_CENTER = 32; // 0x20
+ field public static final int ZONE_ROW_2_LEFT = 16; // 0x10
+ field public static final int ZONE_ROW_2_RIGHT = 64; // 0x40
+ field public static final int ZONE_ROW_3_ALL = 2048; // 0x800
+ field public static final int ZONE_ROW_3_CENTER = 512; // 0x200
+ field public static final int ZONE_ROW_3_LEFT = 256; // 0x100
+ field public static final int ZONE_ROW_3_RIGHT = 1024; // 0x400
+ field public static final int ZONE_ROW_4_ALL = 32768; // 0x8000
+ field public static final int ZONE_ROW_4_CENTER = 8192; // 0x2000
+ field public static final int ZONE_ROW_4_LEFT = 4096; // 0x1000
+ field public static final int ZONE_ROW_4_RIGHT = 16384; // 0x4000
+ }
+
+ public final class VehicleZoneUtil {
+ method public static int getFirstZone(int);
+ method public static int getNextZone(int, int) throws java.lang.IllegalArgumentException;
+ method public static int getNumberOfZones(int);
+ method public static int[] listAllZones(int);
+ method public static int zoneToIndex(int, int) throws java.lang.IllegalArgumentException;
+ }
+
+}
+
+package android.car.app.menu {
+
+ public abstract class CarMenuCallbacks {
+ ctor public CarMenuCallbacks();
+ method public abstract android.car.app.menu.RootMenu getRootMenu(android.os.Bundle);
+ method public abstract void onCarMenuClosed();
+ method public abstract void onCarMenuClosing();
+ method public abstract void onCarMenuOpened();
+ method public abstract void onCarMenuOpening();
+ method public abstract void onItemClicked(java.lang.String);
+ method public abstract boolean onItemLongClicked(java.lang.String);
+ method public abstract boolean onMenuClicked();
+ method public abstract void subscribe(java.lang.String, android.car.app.menu.SubscriptionCallbacks);
+ method public abstract void unsubscribe(java.lang.String, android.car.app.menu.SubscriptionCallbacks);
+ }
+
+ public class CarMenuConstants {
+ ctor public CarMenuConstants();
+ }
+
+ public static class CarMenuConstants.MenuItemConstants {
+ ctor public CarMenuConstants.MenuItemConstants();
+ field public static final int FLAG_BROWSABLE = 1; // 0x1
+ field public static final int FLAG_FIRSTITEM = 2; // 0x2
+ field public static final java.lang.String KEY_EMPTY_PLACEHOLDER = "android.car.app.menu.empty_placeholder";
+ field public static final java.lang.String KEY_FLAGS = "android.car.app.menu.flags";
+ field public static final java.lang.String KEY_ID = "android.car.app.menu.id";
+ field public static final java.lang.String KEY_LEFTICON = "android.car.app.menu.leftIcon";
+ field public static final java.lang.String KEY_REMOTEVIEWS = "android.car.app.menu.remoteViews";
+ field public static final java.lang.String KEY_RIGHTICON = "android.car.app.menu.rightIcon";
+ field public static final java.lang.String KEY_RIGHTTEXT = "android.car.app.menu.rightText";
+ field public static final java.lang.String KEY_TEXT = "android.car.app.menu.text";
+ field public static final java.lang.String KEY_TITLE = "android.car.app.menu.title";
+ field public static final java.lang.String KEY_WIDGET = "android.car.app.menu.widget";
+ field public static final java.lang.String KEY_WIDGET_STATE = "android.car.app.menu.widget_state";
+ field public static final int WIDGET_CHECKBOX = 1; // 0x1
+ field public static final int WIDGET_TEXT_VIEW = 2; // 0x2
+ }
+
+ public static abstract class CarMenuConstants.MenuItemConstants.MenuItemFlags implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class CarMenuConstants.MenuItemConstants.WidgetTypes implements java.lang.annotation.Annotation {
+ }
+
+ public abstract class CarUiEntry {
+ ctor public CarUiEntry(android.content.Context, android.content.Context);
+ method public abstract void closeDrawer();
+ method public abstract android.view.View getContentView();
+ method public abstract int getFragmentContainerId();
+ method public abstract java.lang.CharSequence getSearchBoxText();
+ method public abstract void hideMenuButton();
+ method public abstract void hideTitle();
+ method public abstract void onPause();
+ method public abstract void onRestoreInstanceState(android.os.Bundle);
+ method public abstract void onResume();
+ method public abstract void onSaveInstanceState(android.os.Bundle);
+ method public abstract void onStart();
+ method public abstract void onStop();
+ method public abstract void openDrawer();
+ method public abstract void restoreMenuDrawable();
+ method public abstract void setAutoLightDarkMode();
+ method public abstract void setBackground(android.graphics.Bitmap);
+ method public abstract void setCarMenuCallbacks(android.car.app.menu.CarMenuCallbacks);
+ method public abstract void setDarkMode();
+ method public abstract void setLightMode();
+ method public abstract void setMenuButtonBitmap(android.graphics.Bitmap);
+ method public abstract void setMenuButtonColor(int);
+ method public abstract void setScrimColor(int);
+ method public abstract void setSearchBoxColors(int, int, int, int);
+ method public abstract void setSearchBoxEditListener(android.car.app.menu.SearchBoxEditListener);
+ method public abstract void setSearchBoxEndView(android.view.View);
+ method public abstract void setTitle(java.lang.CharSequence);
+ method public abstract void showMenu(java.lang.String, java.lang.String);
+ method public abstract void showSearchBox(android.view.View.OnClickListener);
+ method public abstract void showTitle();
+ method public abstract void showToast(java.lang.String, long);
+ method public abstract android.widget.EditText startInput(java.lang.String, android.view.View.OnClickListener);
+ method public abstract void stopInput();
+ field protected final android.content.Context mAppContext;
+ field protected final android.content.Context mUiLibContext;
+ }
+
+ public class RootMenu {
+ ctor public RootMenu(java.lang.String);
+ ctor public RootMenu(java.lang.String, android.os.Bundle);
+ method public android.os.Bundle getBundle();
+ method public java.lang.String getId();
+ }
+
+ public abstract class SearchBoxEditListener {
+ ctor public SearchBoxEditListener();
+ method public abstract void onEdit(java.lang.String);
+ method public abstract void onSearch(java.lang.String);
+ }
+
+ public abstract class SubscriptionCallbacks {
+ ctor public SubscriptionCallbacks();
+ method public abstract void onChildChanged(java.lang.String, android.os.Bundle);
+ method public abstract void onChildrenLoaded(java.lang.String, java.util.List<android.os.Bundle>);
+ method public abstract void onError(java.lang.String);
+ }
+
+}
+
+package android.car.cluster.renderer {
+
+ public class DisplayConfiguration implements android.os.Parcelable {
+ ctor public DisplayConfiguration(android.os.Parcel);
+ ctor public DisplayConfiguration(android.graphics.Rect);
+ ctor public DisplayConfiguration(android.graphics.Rect, android.graphics.Rect);
+ method public int describeContents();
+ method public android.graphics.Rect getPrimaryRegion();
+ method public android.graphics.Rect getSecondaryRegion();
+ method public boolean hasSecondaryRegion();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.car.cluster.renderer.DisplayConfiguration> CREATOR;
+ }
+
+ public abstract class InstrumentClusterRenderer {
+ ctor public InstrumentClusterRenderer();
+ method protected abstract android.car.cluster.renderer.NavigationRenderer createNavigationRenderer();
+ method public synchronized android.car.cluster.renderer.NavigationRenderer getNavigationRenderer();
+ method public final synchronized void initialize();
+ method public abstract void onCreate(android.content.Context);
+ method public abstract void onStart();
+ method public abstract void onStop();
+ }
+
+ public abstract class InstrumentClusterRenderingService extends android.app.Service {
+ ctor public InstrumentClusterRenderingService();
+ method protected abstract android.car.cluster.renderer.NavigationRenderer getNavigationRenderer();
+ method public android.os.IBinder onBind(android.content.Intent);
+ method protected void onKeyEvent(android.view.KeyEvent);
+ }
+
+ public abstract class NavigationRenderer {
+ ctor public NavigationRenderer();
+ method public abstract android.car.navigation.CarNavigationInstrumentCluster getNavigationProperties();
+ method public abstract void onNextTurnChanged(int, java.lang.CharSequence, int, int, android.graphics.Bitmap, int);
+ method public abstract void onNextTurnDistanceChanged(int, int, int, int);
+ method public abstract void onStartNavigation();
+ method public abstract void onStopNavigation();
+ }
+
+}
+
+package android.car.content.pm {
+
+ public class AppBlockingPackageInfo implements android.os.Parcelable {
+ ctor public AppBlockingPackageInfo(java.lang.String, int, int, int, android.content.pm.Signature[], java.lang.String[]);
+ ctor public AppBlockingPackageInfo(android.os.Parcel);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.car.content.pm.AppBlockingPackageInfo> CREATOR;
+ field public static final int FLAG_SYSTEM_APP = 1; // 0x1
+ field public static final int FLAG_WHOLE_ACTIVITY = 2; // 0x2
+ field public final java.lang.String[] activities;
+ field public final int flags;
+ field public final int maxRevisionCode;
+ field public final int minRevisionCode;
+ field public final java.lang.String packageName;
+ field public final android.content.pm.Signature[] signatures;
+ }
+
+ public class CarAppBlockingPolicy implements android.os.Parcelable {
+ ctor public CarAppBlockingPolicy(android.car.content.pm.AppBlockingPackageInfo[], android.car.content.pm.AppBlockingPackageInfo[]);
+ ctor public CarAppBlockingPolicy(android.os.Parcel);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.car.content.pm.CarAppBlockingPolicy> CREATOR;
+ field public final android.car.content.pm.AppBlockingPackageInfo[] blacklists;
+ field public final android.car.content.pm.AppBlockingPackageInfo[] whitelists;
+ }
+
+ public abstract class CarAppBlockingPolicyService extends android.app.Service {
+ ctor public CarAppBlockingPolicyService();
+ method protected abstract android.car.content.pm.CarAppBlockingPolicy getAppBlockingPolicy();
+ method public android.os.IBinder onBind(android.content.Intent);
+ field public static final java.lang.String SERVICE_INTERFACE = "android.car.content.pm.CarAppBlockingPolicyService";
+ }
+
+ public final class CarPackageManager {
+ method public boolean isActivityAllowedWhileDriving(java.lang.String, java.lang.String) throws android.car.CarNotConnectedException;
+ method public boolean isActivityBackedBySafeActivity(android.content.ComponentName) throws android.car.CarNotConnectedException;
+ method public boolean isServiceAllowedWhileDriving(java.lang.String, java.lang.String) throws android.car.CarNotConnectedException;
+ method public void setAppBlockingPolicy(java.lang.String, android.car.content.pm.CarAppBlockingPolicy, int) throws android.car.CarNotConnectedException, java.lang.IllegalArgumentException, java.lang.SecurityException;
+ field public static final int FLAG_SET_POLICY_ADD = 2; // 0x2
+ field public static final int FLAG_SET_POLICY_REMOVE = 4; // 0x4
+ field public static final int FLAG_SET_POLICY_WAIT_FOR_CHANGE = 1; // 0x1
+ }
+
+}
+
+package android.car.hardware {
+
+ public class CarPropertyConfig<T> implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getAreaCount();
+ method public int[] getAreaIds();
+ method public int getAreaType();
+ method public int getFirstAndOnlyAreaId();
+ method public T getMaxValue(int);
+ method public T getMaxValue();
+ method public T getMinValue(int);
+ method public T getMinValue();
+ method public int getPropertyId();
+ method public java.lang.Class<T> getPropertyType();
+ method public boolean hasArea(int);
+ method public boolean isGlobalProperty();
+ method public static <T> android.car.hardware.CarPropertyConfig.Builder<T> newBuilder(java.lang.Class<T>, int, int, int);
+ method public static <T> android.car.hardware.CarPropertyConfig.Builder<T> newBuilder(java.lang.Class<T>, int, int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.car.hardware.CarPropertyConfig> CREATOR;
+ }
+
+ public static class CarPropertyConfig.AreaConfig<T> implements android.os.Parcelable {
+ method public int describeContents();
+ method public T getMaxValue();
+ method public T getMinValue();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.car.hardware.CarPropertyConfig.AreaConfig<java.lang.Object>> CREATOR;
+ }
+
+ public static class CarPropertyConfig.Builder<T> {
+ method public android.car.hardware.CarPropertyConfig.Builder<T> addArea(int);
+ method public android.car.hardware.CarPropertyConfig.Builder<T> addAreaConfig(int, T, T);
+ method public android.car.hardware.CarPropertyConfig.Builder<T> addAreas(int[]);
+ method public android.car.hardware.CarPropertyConfig<T> build();
+ }
+
+ public class CarPropertyValue<T> implements android.os.Parcelable {
+ ctor public CarPropertyValue(int, T);
+ ctor public CarPropertyValue(int, int, T);
+ ctor public CarPropertyValue(android.os.Parcel);
+ method public int describeContents();
+ method public int getAreaId();
+ method public int getPropertyId();
+ method public T getValue();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.car.hardware.CarPropertyValue> CREATOR;
+ }
+
+ public class CarSensorEvent implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.car.hardware.CarSensorEvent> CREATOR;
+ field public static final int DRIVE_STATUS_FULLY_RESTRICTED = 31; // 0x1f
+ field public static final int DRIVE_STATUS_LIMIT_MESSAGE_LEN = 16; // 0x10
+ field public static final int DRIVE_STATUS_NO_CONFIG = 8; // 0x8
+ field public static final int DRIVE_STATUS_NO_KEYBOARD_INPUT = 2; // 0x2
+ field public static final int DRIVE_STATUS_NO_VIDEO = 1; // 0x1
+ field public static final int DRIVE_STATUS_NO_VOICE_INPUT = 4; // 0x4
+ field public static final int DRIVE_STATUS_UNRESTRICTED = 0; // 0x0
+ field public static final int GEAR_DRIVE = 100; // 0x64
+ field public static final int GEAR_EIGHTH = 8; // 0x8
+ field public static final int GEAR_FIFTH = 5; // 0x5
+ field public static final int GEAR_FIRST = 1; // 0x1
+ field public static final int GEAR_FOURTH = 4; // 0x4
+ field public static final int GEAR_NEUTRAL = 0; // 0x0
+ field public static final int GEAR_NINTH = 9; // 0x9
+ field public static final int GEAR_PARK = 101; // 0x65
+ field public static final int GEAR_REVERSE = 102; // 0x66
+ field public static final int GEAR_SECOND = 2; // 0x2
+ field public static final int GEAR_SEVENTH = 7; // 0x7
+ field public static final int GEAR_SIXTH = 6; // 0x6
+ field public static final int GEAR_TENTH = 10; // 0xa
+ field public static final int GEAR_THIRD = 3; // 0x3
+ field public static final int IGNITION_STATE_ACC = 3; // 0x3
+ field public static final int IGNITION_STATE_LOCK = 1; // 0x1
+ field public static final int IGNITION_STATE_OFF = 2; // 0x2
+ field public static final int IGNITION_STATE_ON = 4; // 0x4
+ field public static final int IGNITION_STATE_START = 5; // 0x5
+ field public static final int IGNITION_STATE_UNDEFINED = 0; // 0x0
+ field public static final int INDEX_ENVIRONMENT_PRESSURE = 1; // 0x1
+ field public static final int INDEX_ENVIRONMENT_TEMPERATURE = 0; // 0x0
+ field public static final int INDEX_FUEL_LEVEL_IN_DISTANCE = 1; // 0x1
+ field public static final int INDEX_FUEL_LEVEL_IN_PERCENTILE = 0; // 0x0
+ field public static final int INDEX_FUEL_LOW_WARNING = 0; // 0x0
+ field public final float[] floatValues;
+ field public final int[] intValues;
+ field public int sensorType;
+ field public long timestamp;
+ }
+
+ public static class CarSensorEvent.EnvironmentData {
+ field public float pressure;
+ field public float temperature;
+ field public long timestamp;
+ }
+
+ public final class CarSensorManager {
+ method public android.car.hardware.CarSensorEvent getLatestSensorEvent(int) throws android.car.CarNotConnectedException;
+ method public int[] getSupportedSensors() throws android.car.CarNotConnectedException;
+ method public boolean isSensorSupported(int) throws android.car.CarNotConnectedException;
+ method public static boolean isSensorSupported(int[], int);
+ method public boolean registerListener(android.car.hardware.CarSensorManager.OnSensorChangedListener, int, int) throws android.car.CarNotConnectedException, java.lang.IllegalArgumentException;
+ method public void unregisterListener(android.car.hardware.CarSensorManager.OnSensorChangedListener);
+ method public void unregisterListener(android.car.hardware.CarSensorManager.OnSensorChangedListener, int);
+ field public static final int SENSOR_RATE_FAST = 1; // 0x1
+ field public static final int SENSOR_RATE_FASTEST = 0; // 0x0
+ field public static final int SENSOR_RATE_NORMAL = 3; // 0x3
+ field public static final int SENSOR_RATE_UI = 2; // 0x2
+ field public static final int SENSOR_TYPE_CAR_SPEED = 2; // 0x2
+ field public static final int SENSOR_TYPE_DRIVING_STATUS = 11; // 0xb
+ field public static final int SENSOR_TYPE_ENVIRONMENT = 12; // 0xc
+ field public static final int SENSOR_TYPE_FUEL_LEVEL = 5; // 0x5
+ field public static final int SENSOR_TYPE_GEAR = 7; // 0x7
+ field public static final int SENSOR_TYPE_IGNITION_STATE = 22; // 0x16
+ field public static final int SENSOR_TYPE_NIGHT = 9; // 0x9
+ field public static final int SENSOR_TYPE_ODOMETER = 4; // 0x4
+ field public static final int SENSOR_TYPE_PARKING_BRAKE = 6; // 0x6
+ field public static final int SENSOR_TYPE_RPM = 3; // 0x3
+ field public static final int SENSOR_TYPE_VENDOR_EXTENSION_END = 1879048191; // 0x6fffffff
+ }
+
+ public static abstract interface CarSensorManager.OnSensorChangedListener {
+ method public abstract void onSensorChanged(android.car.hardware.CarSensorEvent);
+ }
+
+ public final class CarVendorExtensionManager {
+ method public <E> E getGlobalProperty(java.lang.Class<E>, int) throws android.car.CarNotConnectedException;
+ method public java.util.List<android.car.hardware.CarPropertyConfig> getProperties() throws android.car.CarNotConnectedException;
+ method public <E> E getProperty(java.lang.Class<E>, int, int) throws android.car.CarNotConnectedException;
+ method public void registerCallback(android.car.hardware.CarVendorExtensionManager.CarVendorExtensionCallback) throws android.car.CarNotConnectedException;
+ method public <E> void setGlobalProperty(java.lang.Class<E>, int, E) throws android.car.CarNotConnectedException;
+ method public <E> void setProperty(java.lang.Class<E>, int, int, E) throws android.car.CarNotConnectedException;
+ method public void unregisterCallback(android.car.hardware.CarVendorExtensionManager.CarVendorExtensionCallback);
+ }
+
+ public static abstract interface CarVendorExtensionManager.CarVendorExtensionCallback {
+ method public abstract void onChangeEvent(android.car.hardware.CarPropertyValue);
+ method public abstract void onErrorEvent(int, int);
+ }
+
+}
+
+package android.car.hardware.cabin {
+
+ public final class CarCabinManager {
+ method public boolean getBooleanProperty(int, int) throws android.car.CarNotConnectedException;
+ method public float getFloatProperty(int, int) throws android.car.CarNotConnectedException;
+ method public int getIntProperty(int, int) throws android.car.CarNotConnectedException;
+ method public java.util.List<android.car.hardware.CarPropertyConfig> getPropertyList() throws android.car.CarNotConnectedException;
+ method public static boolean isZonedProperty(int);
+ method public synchronized void registerCallback(android.car.hardware.cabin.CarCabinManager.CarCabinEventCallback) throws android.car.CarNotConnectedException;
+ method public void setBooleanProperty(int, int, boolean) throws android.car.CarNotConnectedException;
+ method public void setFloatProperty(int, int, float) throws android.car.CarNotConnectedException;
+ method public void setIntProperty(int, int, int) throws android.car.CarNotConnectedException;
+ method public synchronized void unregisterCallback(android.car.hardware.cabin.CarCabinManager.CarCabinEventCallback);
+ field public static final int ID_DOOR_LOCK = 3; // 0x3
+ field public static final int ID_DOOR_MOVE = 2; // 0x2
+ field public static final int ID_DOOR_POS = 1; // 0x1
+ field public static final int ID_MIRROR_FOLD = 4102; // 0x1006
+ field public static final int ID_MIRROR_LOCK = 4101; // 0x1005
+ field public static final int ID_MIRROR_Y_MOVE = 4100; // 0x1004
+ field public static final int ID_MIRROR_Y_POS = 4099; // 0x1003
+ field public static final int ID_MIRROR_Z_MOVE = 4098; // 0x1002
+ field public static final int ID_MIRROR_Z_POS = 4097; // 0x1001
+ field public static final int ID_SEAT_BACKREST_ANGLE_1_MOVE = 8201; // 0x2009
+ field public static final int ID_SEAT_BACKREST_ANGLE_1_POS = 8200; // 0x2008
+ field public static final int ID_SEAT_BACKREST_ANGLE_2_MOVE = 8203; // 0x200b
+ field public static final int ID_SEAT_BACKREST_ANGLE_2_POS = 8202; // 0x200a
+ field public static final int ID_SEAT_BELT_BUCKLED = 8195; // 0x2003
+ field public static final int ID_SEAT_BELT_HEIGHT_MOVE = 8197; // 0x2005
+ field public static final int ID_SEAT_BELT_HEIGHT_POS = 8196; // 0x2004
+ field public static final int ID_SEAT_DEPTH_MOVE = 8207; // 0x200f
+ field public static final int ID_SEAT_DEPTH_POS = 8206; // 0x200e
+ field public static final int ID_SEAT_FORE_AFT_MOVE = 8199; // 0x2007
+ field public static final int ID_SEAT_FORE_AFT_POS = 8198; // 0x2006
+ field public static final int ID_SEAT_HEADREST_ANGLE_MOVE = 8217; // 0x2019
+ field public static final int ID_SEAT_HEADREST_ANGLE_POS = 8216; // 0x2018
+ field public static final int ID_SEAT_HEADREST_FORE_AFT_MOVE = 8219; // 0x201b
+ field public static final int ID_SEAT_HEADREST_FORE_AFT_POS = 8218; // 0x201a
+ field public static final int ID_SEAT_HEADREST_HEIGHT_MOVE = 8215; // 0x2017
+ field public static final int ID_SEAT_HEADREST_HEIGHT_POS = 8214; // 0x2016
+ field public static final int ID_SEAT_HEIGHT_MOVE = 8205; // 0x200d
+ field public static final int ID_SEAT_HEIGHT_POS = 8204; // 0x200c
+ field public static final int ID_SEAT_LUMBAR_FORE_AFT_MOVE = 8211; // 0x2013
+ field public static final int ID_SEAT_LUMBAR_FORE_AFT_POS = 8210; // 0x2012
+ field public static final int ID_SEAT_LUMBAR_SIDE_SUPPORT_MOVE = 8213; // 0x2015
+ field public static final int ID_SEAT_LUMBAR_SIDE_SUPPORT_POS = 8212; // 0x2014
+ field public static final int ID_SEAT_MEMORY_SELECT = 8193; // 0x2001
+ field public static final int ID_SEAT_MEMORY_SET = 8194; // 0x2002
+ field public static final int ID_SEAT_TILT_MOVE = 8209; // 0x2011
+ field public static final int ID_SEAT_TILT_POS = 8208; // 0x2010
+ field public static final int ID_WINDOW_LOCK = 12293; // 0x3005
+ field public static final int ID_WINDOW_MOVE = 12290; // 0x3002
+ field public static final int ID_WINDOW_POS = 12289; // 0x3001
+ field public static final int ID_WINDOW_VENT_MOVE = 12292; // 0x3004
+ field public static final int ID_WINDOW_VENT_POS = 12291; // 0x3003
+ }
+
+ public static abstract interface CarCabinManager.CarCabinEventCallback {
+ method public abstract void onChangeEvent(android.car.hardware.CarPropertyValue);
+ method public abstract void onErrorEvent(int, int);
+ }
+
+}
+
+package android.car.hardware.hvac {
+
+ public final class CarHvacManager {
+ method public boolean getBooleanProperty(int, int) throws android.car.CarNotConnectedException;
+ method public float getFloatProperty(int, int) throws android.car.CarNotConnectedException;
+ method public int getIntProperty(int, int) throws android.car.CarNotConnectedException;
+ method public java.util.List<android.car.hardware.CarPropertyConfig> getPropertyList() throws android.car.CarNotConnectedException;
+ method public static boolean isZonedProperty(int);
+ method public synchronized void registerCallback(android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback) throws android.car.CarNotConnectedException;
+ method public void setBooleanProperty(int, int, boolean) throws android.car.CarNotConnectedException;
+ method public void setFloatProperty(int, int, float) throws android.car.CarNotConnectedException;
+ method public void setIntProperty(int, int, int) throws android.car.CarNotConnectedException;
+ method public synchronized void unregisterCallback(android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback);
+ field public static final int FAN_POSITION_DEFROST = 4; // 0x4
+ field public static final int FAN_POSITION_DEFROST_AND_FLOOR = 5; // 0x5
+ field public static final int FAN_POSITION_FACE = 1; // 0x1
+ field public static final int FAN_POSITION_FACE_AND_FLOOR = 3; // 0x3
+ field public static final int FAN_POSITION_FLOOR = 2; // 0x2
+ field public static final int ID_MIRROR_DEFROSTER_ON = 1; // 0x1
+ field public static final int ID_OUTSIDE_AIR_TEMP = 3; // 0x3
+ field public static final int ID_STEERING_WHEEL_TEMP = 2; // 0x2
+ field public static final int ID_TEMPERATURE_UNITS = 4; // 0x4
+ field public static final int ID_WINDOW_DEFROSTER_ON = 20481; // 0x5001
+ field public static final int ID_ZONED_AC_ON = 16393; // 0x4009
+ field public static final int ID_ZONED_AIR_RECIRCULATION_ON = 16395; // 0x400b
+ field public static final int ID_ZONED_AUTOMATIC_MODE_ON = 16394; // 0x400a
+ field public static final int ID_ZONED_DUAL_ZONE_ON = 16397; // 0x400d
+ field public static final int ID_ZONED_FAN_POSITION = 16391; // 0x4007
+ field public static final int ID_ZONED_FAN_POSITION_AVAILABLE = 16390; // 0x4006
+ field public static final int ID_ZONED_FAN_SPEED_RPM = 16389; // 0x4005
+ field public static final int ID_ZONED_FAN_SPEED_SETPOINT = 16388; // 0x4004
+ field public static final int ID_ZONED_HVAC_POWER_ON = 16387; // 0x4003
+ field public static final int ID_ZONED_MAX_AC_ON = 16396; // 0x400c
+ field public static final int ID_ZONED_MAX_DEFROST_ON = 16398; // 0x400e
+ field public static final int ID_ZONED_SEAT_TEMP = 16392; // 0x4008
+ field public static final int ID_ZONED_TEMP_ACTUAL = 16386; // 0x4002
+ field public static final int ID_ZONED_TEMP_SETPOINT = 16385; // 0x4001
+ }
+
+ public static abstract interface CarHvacManager.CarHvacEventCallback {
+ method public abstract void onChangeEvent(android.car.hardware.CarPropertyValue);
+ method public abstract void onErrorEvent(int, int);
+ }
+
+}
+
+package android.car.hardware.radio {
+
+ public class CarRadioEvent implements android.os.Parcelable {
+ ctor public CarRadioEvent(int, android.car.hardware.radio.CarRadioPreset);
+ method public int describeContents();
+ method public int getEventType();
+ method public android.car.hardware.radio.CarRadioPreset getPreset();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.car.hardware.radio.CarRadioEvent> CREATOR;
+ field public static final int RADIO_PRESET = 0; // 0x0
+ }
+
+ public final class CarRadioManager {
+ method public android.car.hardware.radio.CarRadioPreset getPreset(int) throws android.car.CarNotConnectedException;
+ method public int getPresetCount() throws android.car.CarNotConnectedException;
+ method public synchronized void registerListener(android.car.hardware.radio.CarRadioManager.CarRadioEventListener) throws android.car.CarNotConnectedException;
+ method public boolean setPreset(android.car.hardware.radio.CarRadioPreset) throws android.car.CarNotConnectedException, java.lang.IllegalArgumentException;
+ method public synchronized void unregisterListener();
+ }
+
+ public static abstract interface CarRadioManager.CarRadioEventListener {
+ method public abstract void onEvent(android.car.hardware.radio.CarRadioEvent);
+ }
+
+ public class CarRadioPreset implements android.os.Parcelable {
+ ctor public CarRadioPreset(int, int, int, int);
+ method public int describeContents();
+ method public int getBand();
+ method public int getChannel();
+ method public int getPresetNumber();
+ method public int getSubChannel();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.car.hardware.radio.CarRadioPreset> CREATOR;
+ }
+
+}
+
+package android.car.input {
+
+ public abstract class CarInputHandlingService extends android.app.Service {
+ ctor protected CarInputHandlingService(android.car.input.CarInputHandlingService.InputFilter[]);
+ method public android.os.IBinder onBind(android.content.Intent);
+ method protected abstract void onKeyEvent(android.view.KeyEvent, int);
+ field public static final int INPUT_CALLBACK_BINDER_CODE = 1; // 0x1
+ field public static final java.lang.String INPUT_CALLBACK_BINDER_KEY = "callback_binder";
+ }
+
+ public static class CarInputHandlingService.InputFilter implements android.os.Parcelable {
+ ctor public CarInputHandlingService.InputFilter(int, int);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public final int mKeyCode;
+ field public final int mTargetDisplay;
+ }
+
+}
+
+package android.car.media {
+
+ public final class CarAudioManager {
+ method public void abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
+ method public android.media.AudioAttributes getAudioAttributesForCarUsage(int) throws android.car.CarNotConnectedException;
+ method public int getStreamMaxVolume(int) throws android.car.CarNotConnectedException;
+ method public int getStreamMinVolume(int) throws android.car.CarNotConnectedException;
+ method public int getStreamVolume(int) throws android.car.CarNotConnectedException;
+ method public boolean isMediaMuted() throws android.car.CarNotConnectedException;
+ method public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int) throws android.car.CarNotConnectedException, java.lang.IllegalArgumentException;
+ method public boolean setMediaMute(boolean) throws android.car.CarNotConnectedException;
+ method public void setStreamVolume(int, int, int) throws android.car.CarNotConnectedException;
+ method public void setVolumeController(android.media.IVolumeController) throws android.car.CarNotConnectedException;
+ field public static final int CAR_AUDIO_USAGE_ALARM = 6; // 0x6
+ field public static final int CAR_AUDIO_USAGE_DEFAULT = 0; // 0x0
+ field public static final int CAR_AUDIO_USAGE_MUSIC = 1; // 0x1
+ field public static final int CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE = 3; // 0x3
+ field public static final int CAR_AUDIO_USAGE_NOTIFICATION = 7; // 0x7
+ field public static final int CAR_AUDIO_USAGE_RADIO = 2; // 0x2
+ field public static final int CAR_AUDIO_USAGE_SYSTEM_SAFETY_ALERT = 9; // 0x9
+ field public static final int CAR_AUDIO_USAGE_SYSTEM_SOUND = 8; // 0x8
+ field public static final int CAR_AUDIO_USAGE_VOICE_CALL = 4; // 0x4
+ field public static final int CAR_AUDIO_USAGE_VOICE_COMMAND = 5; // 0x5
+ }
+
+}
+
+package android.car.navigation {
+
+ public class CarNavigationInstrumentCluster implements android.os.Parcelable {
+ ctor public CarNavigationInstrumentCluster(android.car.navigation.CarNavigationInstrumentCluster);
+ method public static android.car.navigation.CarNavigationInstrumentCluster createCluster(int);
+ method public static android.car.navigation.CarNavigationInstrumentCluster createCustomImageCluster(int, int, int, int);
+ method public int describeContents();
+ method public int getImageColorDepthBits();
+ method public int getImageHeight();
+ method public int getImageWidth();
+ method public int getMinIntervalMillis();
+ method public int getType();
+ method public boolean supportsCustomImages();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CLUSTER_TYPE_CUSTOM_IMAGES_SUPPORTED = 1; // 0x1
+ field public static final int CLUSTER_TYPE_IMAGE_CODES_ONLY = 2; // 0x2
+ field public static final android.os.Parcelable.Creator<android.car.navigation.CarNavigationInstrumentCluster> CREATOR;
+ }
+
+}
+
+package android.car.settings {
+
+ public class CarSettings {
+ ctor public CarSettings();
+ }
+
+ public static final class CarSettings.Global {
+ ctor public CarSettings.Global();
+ field public static final java.lang.String KEY_GARAGE_MODE_ENABLED = "android.car.GARAGE_MODE_ENABLED";
+ field public static final java.lang.String KEY_GARAGE_MODE_MAINTENANCE_WINDOW = "android.car.GARAGE_MODE_MAINTENANCE_WINDOW";
+ field public static final java.lang.String KEY_GARAGE_MODE_WAKE_UP_TIME = "android.car.GARAGE_MODE_WAKE_UP_TIME";
+ }
+
+}
+
+package android.car.test {
+
+ public class CarTestManagerBinderWrapper {
+ ctor public CarTestManagerBinderWrapper(android.os.IBinder);
+ method public void onCarDisconnected();
+ field public final android.os.IBinder binder;
+ }
+
+}
+
diff --git a/car-lib/api/system-current.txt b/car-lib/api/system-current.txt
index b846481..6f32dd7 100644
--- a/car-lib/api/system-current.txt
+++ b/car-lib/api/system-current.txt
@@ -12,12 +12,17 @@
field public static final java.lang.String APP_FOCUS_SERVICE = "app_focus";
field public static final java.lang.String AUDIO_SERVICE = "audio";
field public static final java.lang.String CABIN_SERVICE = "cabin";
+ field public static final java.lang.String CAR_EXTRA_MEDIA_PACKAGE = "android.car.intent.extra.MEDIA_PACKAGE";
+ field public static final java.lang.String CAR_INTENT_ACTION_MEDIA_TEMPLATE = "android.car.intent.action.MEDIA_TEMPLATE";
field public static final int CONNECTION_TYPE_EMBEDDED = 5; // 0x5
+ field public static final java.lang.String DIAGNOSTIC_SERVICE = "diagnostic";
field public static final java.lang.String HVAC_SERVICE = "hvac";
field public static final java.lang.String INFO_SERVICE = "info";
field public static final java.lang.String PACKAGE_SERVICE = "package";
field public static final java.lang.String PERMISSION_CAR_CABIN = "android.car.permission.CAR_CABIN";
field public static final java.lang.String PERMISSION_CAR_CONTROL_AUDIO_VOLUME = "android.car.permission.CAR_CONTROL_AUDIO_VOLUME";
+ field public static final java.lang.String PERMISSION_CAR_DIAGNOSTIC_CLEAR = "android.car.permission.DIAGNOSTIC_CLEAR";
+ field public static final java.lang.String PERMISSION_CAR_DIAGNOSTIC_READ_ALL = "android.car.permission.DIAGNOSTIC_READ_ALL";
field public static final java.lang.String PERMISSION_CAR_HVAC = "android.car.permission.CAR_HVAC";
field public static final java.lang.String PERMISSION_CAR_PROJECTION = "android.car.permission.CAR_PROJECTION";
field public static final java.lang.String PERMISSION_CAR_RADIO = "android.car.permission.CAR_RADIO";
@@ -27,13 +32,14 @@
field public static final java.lang.String PERMISSION_MILEAGE = "android.car.permission.CAR_MILEAGE";
field public static final deprecated java.lang.String PERMISSION_MOCK_VEHICLE_HAL = "android.car.permission.CAR_MOCK_VEHICLE_HAL";
field public static final java.lang.String PERMISSION_SPEED = "android.car.permission.CAR_SPEED";
+ field public static final java.lang.String PERMISSION_VEHICLE_DYNAMICS_STATE = "android.car.permission.VEHICLE_DYNAMICS_STATE";
field public static final java.lang.String PERMISSION_VENDOR_EXTENSION = "android.car.permission.CAR_VENDOR_EXTENSION";
field public static final java.lang.String PROJECTION_SERVICE = "projection";
field public static final java.lang.String RADIO_SERVICE = "radio";
field public static final java.lang.String SENSOR_SERVICE = "sensor";
field public static final java.lang.String TEST_SERVICE = "car-service-test";
field public static final java.lang.String VENDOR_EXTENSION_SERVICE = "vendor_extension";
- field public static final int VERSION = 2; // 0x2
+ field public static final int VERSION = 3; // 0x3
}
public final class CarAppFocusManager {
@@ -75,10 +81,10 @@
public final class CarProjectionManager {
method public void onCarDisconnected();
+ method public void registerProjectionListener(android.car.CarProjectionManager.CarProjectionListener, int) throws android.car.CarNotConnectedException;
method public void registerProjectionRunner(android.content.Intent) throws android.car.CarNotConnectedException;
- method public void regsiterProjectionListener(android.car.CarProjectionManager.CarProjectionListener, int) throws android.car.CarNotConnectedException;
+ method public void unregisterProjectionListener();
method public void unregisterProjectionRunner(android.content.Intent);
- method public void unregsiterProjectionListener();
field public static final int PROJECTION_LONG_PRESS_VOICE_SEARCH = 2; // 0x2
field public static final int PROJECTION_VOICE_SEARCH = 1; // 0x1
}
@@ -274,18 +280,6 @@
package android.car.cluster.renderer {
- public class DisplayConfiguration implements android.os.Parcelable {
- ctor public DisplayConfiguration(android.os.Parcel);
- ctor public DisplayConfiguration(android.graphics.Rect);
- ctor public DisplayConfiguration(android.graphics.Rect, android.graphics.Rect);
- method public int describeContents();
- method public android.graphics.Rect getPrimaryRegion();
- method public android.graphics.Rect getSecondaryRegion();
- method public boolean hasSecondaryRegion();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.car.cluster.renderer.DisplayConfiguration> CREATOR;
- }
-
public abstract class InstrumentClusterRenderer {
ctor public InstrumentClusterRenderer();
method protected abstract android.car.cluster.renderer.NavigationRenderer createNavigationRenderer();
@@ -361,6 +355,265 @@
}
+package android.car.diagnostic {
+
+ public class CarDiagnosticEvent implements android.os.Parcelable {
+ ctor public CarDiagnosticEvent(android.os.Parcel);
+ method public int describeContents();
+ method public java.lang.Integer getFuelSystemStatus();
+ method public java.lang.Integer getFuelType();
+ method public android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors getIgnitionMonitors();
+ method public java.lang.Integer getSecondaryAirStatus();
+ method public float getSystemFloatSensor(int, float);
+ method public java.lang.Float getSystemFloatSensor(int);
+ method public int getSystemIntegerSensor(int, int);
+ method public java.lang.Integer getSystemIntegerSensor(int);
+ method public float getVendorFloatSensor(int, float);
+ method public java.lang.Float getVendorFloatSensor(int);
+ method public int getVendorIntegerSensor(int, int);
+ method public java.lang.Integer getVendorIntegerSensor(int);
+ method public boolean isFreezeFrame();
+ method public boolean isLiveFrame();
+ method public void writeToJson(android.util.JsonWriter) throws java.io.IOException;
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.car.diagnostic.CarDiagnosticEvent> CREATOR;
+ field public final java.lang.String dtc;
+ field public final int frameType;
+ field public final long timestamp;
+ }
+
+ public static class CarDiagnosticEvent.Builder {
+ method public android.car.diagnostic.CarDiagnosticEvent.Builder atTimestamp(long);
+ method public android.car.diagnostic.CarDiagnosticEvent build();
+ method public static android.car.diagnostic.CarDiagnosticEvent.Builder newFreezeFrameBuilder();
+ method public static android.car.diagnostic.CarDiagnosticEvent.Builder newLiveFrameBuilder();
+ method public android.car.diagnostic.CarDiagnosticEvent.Builder withDtc(java.lang.String);
+ method public android.car.diagnostic.CarDiagnosticEvent.Builder withFloatValue(int, float);
+ method public android.car.diagnostic.CarDiagnosticEvent.Builder withIntValue(int, int);
+ }
+
+ public static class CarDiagnosticEvent.CommonIgnitionMonitors {
+ method public android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors asCompressionIgnitionMonitors();
+ method public android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors asSparkIgnitionMonitors();
+ field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor components;
+ field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor fuelSystem;
+ field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor misfire;
+ }
+
+ public static final class CarDiagnosticEvent.CompressionIgnitionMonitors extends android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors {
+ field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor EGROrVVT;
+ field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor NMHCCatalyst;
+ field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor NOxSCR;
+ field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor PMFilter;
+ field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor boostPressure;
+ field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor exhaustGasSensor;
+ }
+
+ public static final class CarDiagnosticEvent.FuelSystemStatus {
+ field public static final int CLOSED_LOOP = 2; // 0x2
+ field public static final int CLOSED_LOOP_BUT_FEEDBACK_FAULT = 16; // 0x10
+ field public static final int OPEN_ENGINE_LOAD_OR_DECELERATION = 4; // 0x4
+ field public static final int OPEN_INSUFFICIENT_ENGINE_TEMPERATURE = 1; // 0x1
+ field public static final int OPEN_SYSTEM_FAILURE = 8; // 0x8
+ }
+
+ public static abstract class CarDiagnosticEvent.FuelSystemStatus.Status implements java.lang.annotation.Annotation {
+ }
+
+ public static final class CarDiagnosticEvent.FuelType {
+ field public static final int BIFUEL_RUNNING_CNG = 13; // 0xd
+ field public static final int BIFUEL_RUNNING_DIESEL = 23; // 0x17
+ field public static final int BIFUEL_RUNNING_ELECTRIC = 15; // 0xf
+ field public static final int BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION = 16; // 0x10
+ field public static final int BIFUEL_RUNNING_ETHANOL = 11; // 0xb
+ field public static final int BIFUEL_RUNNING_GASOLINE = 9; // 0x9
+ field public static final int BIFUEL_RUNNING_LPG = 12; // 0xc
+ field public static final int BIFUEL_RUNNING_METHANOL = 10; // 0xa
+ field public static final int BIFUEL_RUNNING_PROPANE = 14; // 0xe
+ field public static final int CNG = 6; // 0x6
+ field public static final int DIESEL = 4; // 0x4
+ field public static final int ELECTRIC = 8; // 0x8
+ field public static final int ETHANOL = 3; // 0x3
+ field public static final int GASOLINE = 1; // 0x1
+ field public static final int HYBRID_DIESEL = 19; // 0x13
+ field public static final int HYBRID_ELECTRIC = 20; // 0x14
+ field public static final int HYBRID_ETHANOL = 18; // 0x12
+ field public static final int HYBRID_GASOLINE = 17; // 0x11
+ field public static final int HYBRID_REGENERATIVE = 22; // 0x16
+ field public static final int HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION = 21; // 0x15
+ field public static final int LPG = 5; // 0x5
+ field public static final int METHANOL = 2; // 0x2
+ field public static final int NOT_AVAILABLE = 0; // 0x0
+ field public static final int PROPANE = 7; // 0x7
+ }
+
+ public static abstract class CarDiagnosticEvent.FuelType.Type implements java.lang.annotation.Annotation {
+ }
+
+ public static final class CarDiagnosticEvent.IgnitionMonitor {
+ field public final boolean available;
+ field public final boolean incomplete;
+ }
+
+ public static final class CarDiagnosticEvent.SecondaryAirStatus {
+ field public static final int DOWNSTREAM_OF_CATALYCIC_CONVERTER = 2; // 0x2
+ field public static final int FROM_OUTSIDE_OR_OFF = 4; // 0x4
+ field public static final int PUMP_ON_FOR_DIAGNOSTICS = 8; // 0x8
+ field public static final int UPSTREAM = 1; // 0x1
+ }
+
+ public static abstract class CarDiagnosticEvent.SecondaryAirStatus.Status implements java.lang.annotation.Annotation {
+ }
+
+ public static final class CarDiagnosticEvent.SparkIgnitionMonitors extends android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors {
+ field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor ACRefrigerant;
+ field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor EGR;
+ field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor catalyst;
+ field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor evaporativeSystem;
+ field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor heatedCatalyst;
+ field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor oxygenSensor;
+ field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor oxygenSensorHeater;
+ field public final android.car.diagnostic.CarDiagnosticEvent.IgnitionMonitor secondaryAirSystem;
+ }
+
+ public final class CarDiagnosticManager {
+ method public boolean clearFreezeFrames(long...) throws android.car.CarNotConnectedException;
+ method public android.car.diagnostic.CarDiagnosticEvent getFreezeFrame(long) throws android.car.CarNotConnectedException;
+ method public long[] getFreezeFrameTimestamps() throws android.car.CarNotConnectedException;
+ method public android.car.diagnostic.CarDiagnosticEvent getLatestLiveFrame() throws android.car.CarNotConnectedException;
+ method public boolean isClearFreezeFramesSupported() throws android.car.CarNotConnectedException;
+ method public boolean isFreezeFrameNotificationSupported() throws android.car.CarNotConnectedException;
+ method public boolean isGetFreezeFrameSupported() throws android.car.CarNotConnectedException;
+ method public boolean isLiveFrameSupported() throws android.car.CarNotConnectedException;
+ method public void onCarDisconnected();
+ method public boolean registerListener(android.car.diagnostic.CarDiagnosticManager.OnDiagnosticEventListener, int, int) throws android.car.CarNotConnectedException, java.lang.IllegalArgumentException;
+ method public void unregisterListener(android.car.diagnostic.CarDiagnosticManager.OnDiagnosticEventListener);
+ field public static final int FRAME_TYPE_FREEZE = 1; // 0x1
+ field public static final int FRAME_TYPE_LIVE = 0; // 0x0
+ }
+
+ public static abstract class CarDiagnosticManager.FrameType implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract interface CarDiagnosticManager.OnDiagnosticEventListener {
+ method public abstract void onDiagnosticEvent(android.car.diagnostic.CarDiagnosticEvent);
+ }
+
+ public final class FloatSensorIndex {
+ field public static final int ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 58; // 0x3a
+ field public static final int ABSOLUTE_LOAD_VALUE = 48; // 0x30
+ field public static final int ABSOLUTE_THROTTLE_POSITION_B = 51; // 0x33
+ field public static final int ABSOLUTE_THROTTLE_POSITION_C = 52; // 0x34
+ field public static final int ACCELERATOR_PEDAL_POSITION_D = 53; // 0x35
+ field public static final int ACCELERATOR_PEDAL_POSITION_E = 54; // 0x36
+ field public static final int ACCELERATOR_PEDAL_POSITION_F = 55; // 0x37
+ field public static final int CALCULATED_ENGINE_LOAD = 0; // 0x0
+ field public static final int CATALYST_TEMPERATURE_BANK1_SENSOR1 = 44; // 0x2c
+ field public static final int CATALYST_TEMPERATURE_BANK1_SENSOR2 = 46; // 0x2e
+ field public static final int CATALYST_TEMPERATURE_BANK2_SENSOR1 = 45; // 0x2d
+ field public static final int CATALYST_TEMPERATURE_BANK2_SENSOR2 = 47; // 0x2f
+ field public static final int COMMANDED_EVAPORATIVE_PURGE = 41; // 0x29
+ field public static final int COMMANDED_EXHAUST_GAS_RECIRCULATION = 39; // 0x27
+ field public static final int COMMANDED_THROTTLE_ACTUATOR = 56; // 0x38
+ field public static final int ENGINE_COOLANT_TEMPERATURE = 1; // 0x1
+ field public static final int ENGINE_FUEL_RATE = 70; // 0x46
+ field public static final int ENGINE_RPM = 8; // 0x8
+ field public static final int ETHANOL_FUEL_PERCENTAGE = 57; // 0x39
+ field public static final int EVAPORATION_SYSTEM_VAPOR_PRESSURE = 43; // 0x2b
+ field public static final int EXHAUST_GAS_RECIRCULATION_ERROR = 40; // 0x28
+ field public static final int FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO = 49; // 0x31
+ field public static final int FUEL_INJECTION_TIMING = 69; // 0x45
+ field public static final int FUEL_PRESSURE = 6; // 0x6
+ field public static final int FUEL_RAIL_GAUGE_PRESSURE = 38; // 0x26
+ field public static final int FUEL_RAIL_PRESSURE = 37; // 0x25
+ field public static final int FUEL_TANK_LEVEL_INPUT = 42; // 0x2a
+ field public static final int HYBRID_BATTERY_PACK_REMAINING_LIFE = 68; // 0x44
+ field public static final int INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 7; // 0x7
+ field public static final int LAST_SYSTEM = 70; // 0x46
+ field public static final int LONG_TERM_FUEL_TRIM_BANK1 = 3; // 0x3
+ field public static final int LONG_TERM_FUEL_TRIM_BANK2 = 5; // 0x5
+ field public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 63; // 0x3f
+ field public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 64; // 0x40
+ field public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 65; // 0x41
+ field public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 66; // 0x42
+ field public static final int MAF_AIR_FLOW_RATE = 11; // 0xb
+ field public static final int OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO = 15; // 0xf
+ field public static final int OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM = 14; // 0xe
+ field public static final int OXYGEN_SENSOR1_VOLTAGE = 13; // 0xd
+ field public static final int OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO = 18; // 0x12
+ field public static final int OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM = 17; // 0x11
+ field public static final int OXYGEN_SENSOR2_VOLTAGE = 16; // 0x10
+ field public static final int OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO = 21; // 0x15
+ field public static final int OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM = 20; // 0x14
+ field public static final int OXYGEN_SENSOR3_VOLTAGE = 19; // 0x13
+ field public static final int OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO = 24; // 0x18
+ field public static final int OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM = 23; // 0x17
+ field public static final int OXYGEN_SENSOR4_VOLTAGE = 22; // 0x16
+ field public static final int OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO = 27; // 0x1b
+ field public static final int OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM = 26; // 0x1a
+ field public static final int OXYGEN_SENSOR5_VOLTAGE = 25; // 0x19
+ field public static final int OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO = 30; // 0x1e
+ field public static final int OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM = 29; // 0x1d
+ field public static final int OXYGEN_SENSOR6_VOLTAGE = 28; // 0x1c
+ field public static final int OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO = 33; // 0x21
+ field public static final int OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM = 32; // 0x20
+ field public static final int OXYGEN_SENSOR7_VOLTAGE = 31; // 0x1f
+ field public static final int OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO = 36; // 0x24
+ field public static final int OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM = 35; // 0x23
+ field public static final int OXYGEN_SENSOR8_VOLTAGE = 34; // 0x22
+ field public static final int RELATIVE_ACCELERATOR_PEDAL_POSITION = 67; // 0x43
+ field public static final int RELATIVE_THROTTLE_POSITION = 50; // 0x32
+ field public static final int SHORT_TERM_FUEL_TRIM_BANK1 = 2; // 0x2
+ field public static final int SHORT_TERM_FUEL_TRIM_BANK2 = 4; // 0x4
+ field public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 59; // 0x3b
+ field public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 60; // 0x3c
+ field public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 61; // 0x3d
+ field public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 62; // 0x3e
+ field public static final int THROTTLE_POSITION = 12; // 0xc
+ field public static final int TIMING_ADVANCE = 10; // 0xa
+ field public static final int VEHICLE_SPEED = 9; // 0x9
+ field public static final int VENDOR_START = 71; // 0x47
+ }
+
+ public final class IntegerSensorIndex {
+ field public static final int ABSOLUTE_BAROMETRIC_PRESSURE = 11; // 0xb
+ field public static final int AMBIENT_AIR_TEMPERATURE = 13; // 0xd
+ field public static final int COMMANDED_SECONDARY_AIR_STATUS = 5; // 0x5
+ field public static final int CONTROL_MODULE_VOLTAGE = 12; // 0xc
+ field public static final int DISTANCE_TRAVELED_SINCE_CODES_CLEARED = 10; // 0xa
+ field public static final int DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON = 8; // 0x8
+ field public static final int DRIVER_DEMAND_PERCENT_TORQUE = 24; // 0x18
+ field public static final int ENGINE_ACTUAL_PERCENT_TORQUE = 25; // 0x19
+ field public static final int ENGINE_OIL_TEMPERATURE = 23; // 0x17
+ field public static final int ENGINE_PERCENT_TORQUE_DATA_IDLE = 27; // 0x1b
+ field public static final int ENGINE_PERCENT_TORQUE_DATA_POINT1 = 28; // 0x1c
+ field public static final int ENGINE_PERCENT_TORQUE_DATA_POINT2 = 29; // 0x1d
+ field public static final int ENGINE_PERCENT_TORQUE_DATA_POINT3 = 30; // 0x1e
+ field public static final int ENGINE_PERCENT_TORQUE_DATA_POINT4 = 31; // 0x1f
+ field public static final int ENGINE_REFERENCE_PERCENT_TORQUE = 26; // 0x1a
+ field public static final int FUEL_RAIL_ABSOLUTE_PRESSURE = 22; // 0x16
+ field public static final int FUEL_SYSTEM_STATUS = 0; // 0x0
+ field public static final int FUEL_TYPE = 21; // 0x15
+ field public static final int IGNITION_MONITORS_SUPPORTED = 2; // 0x2
+ field public static final int IGNITION_SPECIFIC_MONITORS = 3; // 0x3
+ field public static final int INTAKE_AIR_TEMPERATURE = 4; // 0x4
+ field public static final int LAST_SYSTEM = 31; // 0x1f
+ field public static final int MALFUNCTION_INDICATOR_LIGHT_ON = 1; // 0x1
+ field public static final int MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR = 20; // 0x14
+ field public static final int MAX_FUEL_AIR_EQUIVALENCE_RATIO = 16; // 0x10
+ field public static final int MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 19; // 0x13
+ field public static final int MAX_OXYGEN_SENSOR_CURRENT = 18; // 0x12
+ field public static final int MAX_OXYGEN_SENSOR_VOLTAGE = 17; // 0x11
+ field public static final int NUM_OXYGEN_SENSORS_PRESENT = 6; // 0x6
+ field public static final int RUNTIME_SINCE_ENGINE_START = 7; // 0x7
+ field public static final int TIME_SINCE_TROUBLE_CODES_CLEARED = 15; // 0xf
+ field public static final int TIME_WITH_MALFUNCTION_LIGHT_ON = 14; // 0xe
+ field public static final int VENDOR_START = 32; // 0x20
+ field public static final int WARMUPS_SINCE_CODES_CLEARED = 9; // 0x9
+ }
+
+}
+
package android.car.hardware {
public class CarPropertyConfig<T> implements android.os.Parcelable {
@@ -446,8 +699,14 @@
field public static final int INDEX_FUEL_LEVEL_IN_DISTANCE = 1; // 0x1
field public static final int INDEX_FUEL_LEVEL_IN_PERCENTILE = 0; // 0x0
field public static final int INDEX_FUEL_LOW_WARNING = 0; // 0x0
+ field public static final int INDEX_WHEEL_DISTANCE_FRONT_LEFT = 1; // 0x1
+ field public static final int INDEX_WHEEL_DISTANCE_FRONT_RIGHT = 2; // 0x2
+ field public static final int INDEX_WHEEL_DISTANCE_REAR_LEFT = 4; // 0x4
+ field public static final int INDEX_WHEEL_DISTANCE_REAR_RIGHT = 3; // 0x3
+ field public static final int INDEX_WHEEL_DISTANCE_RESET_COUNT = 0; // 0x0
field public final float[] floatValues;
field public final int[] intValues;
+ field public final long[] longValues;
field public int sensorType;
field public long timestamp;
}
@@ -470,6 +729,7 @@
field public static final int SENSOR_RATE_FASTEST = 0; // 0x0
field public static final int SENSOR_RATE_NORMAL = 3; // 0x3
field public static final int SENSOR_RATE_UI = 2; // 0x2
+ field public static final int SENSOR_TYPE_ABS_ACTIVE = 24; // 0x18
field public static final int SENSOR_TYPE_CAR_SPEED = 2; // 0x2
field public static final int SENSOR_TYPE_DRIVING_STATUS = 11; // 0xb
field public static final int SENSOR_TYPE_ENVIRONMENT = 12; // 0xc
@@ -480,7 +740,9 @@
field public static final int SENSOR_TYPE_ODOMETER = 4; // 0x4
field public static final int SENSOR_TYPE_PARKING_BRAKE = 6; // 0x6
field public static final int SENSOR_TYPE_RPM = 3; // 0x3
+ field public static final int SENSOR_TYPE_TRACTION_CONTROL_ACTIVE = 25; // 0x19
field public static final int SENSOR_TYPE_VENDOR_EXTENSION_END = 1879048191; // 0x6fffffff
+ field public static final int SENSOR_TYPE_WHEEL_TICK_DISTANCE = 23; // 0x17
}
public static abstract interface CarSensorManager.OnSensorChangedListener {
@@ -598,6 +860,7 @@
field public static final int ID_ZONED_FAN_POSITION_AVAILABLE = 16390; // 0x4006
field public static final int ID_ZONED_FAN_SPEED_RPM = 16389; // 0x4005
field public static final int ID_ZONED_FAN_SPEED_SETPOINT = 16388; // 0x4004
+ field public static final int ID_ZONED_HVAC_AUTO_RECIRC_ON = 16399; // 0x400f
field public static final int ID_ZONED_HVAC_POWER_ON = 16387; // 0x4003
field public static final int ID_ZONED_MAX_AC_ON = 16396; // 0x400c
field public static final int ID_ZONED_MAX_DEFROST_ON = 16398; // 0x400e
@@ -690,6 +953,7 @@
field public static final int CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE = 3; // 0x3
field public static final int CAR_AUDIO_USAGE_NOTIFICATION = 7; // 0x7
field public static final int CAR_AUDIO_USAGE_RADIO = 2; // 0x2
+ field public static final int CAR_AUDIO_USAGE_RINGTONE = 10; // 0xa
field public static final int CAR_AUDIO_USAGE_SYSTEM_SAFETY_ALERT = 9; // 0x9
field public static final int CAR_AUDIO_USAGE_SYSTEM_SOUND = 8; // 0x8
field public static final int CAR_AUDIO_USAGE_VOICE_CALL = 4; // 0x4
diff --git a/car-lib/src/android/car/Car.java b/car-lib/src/android/car/Car.java
index 6ed5d73..c0b83f7 100644
--- a/car-lib/src/android/car/Car.java
+++ b/car-lib/src/android/car/Car.java
@@ -20,8 +20,9 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.car.annotation.FutureFeature;
+import android.car.cluster.CarInstrumentClusterManager;
import android.car.content.pm.CarPackageManager;
-import android.car.hardware.CarDiagnosticManager;
+import android.car.diagnostic.CarDiagnosticManager;
import android.car.hardware.CarSensorManager;
import android.car.hardware.CarVendorExtensionManager;
import android.car.hardware.cabin.CarCabinManager;
@@ -29,6 +30,7 @@
import android.car.hardware.radio.CarRadioManager;
import android.car.media.CarAudioManager;
import android.car.navigation.CarNavigationStatusManager;
+import android.car.CarBluetoothManager;
import android.car.test.CarTestManagerBinderWrapper;
import android.car.vms.VmsSubscriberManager;
import android.content.ComponentName;
@@ -44,7 +46,6 @@
import android.util.Log;
import com.android.car.internal.FeatureConfiguration;
-import com.android.car.internal.FeatureUtil;
import com.android.internal.annotations.GuardedBy;
import java.lang.annotation.Retention;
@@ -62,8 +63,9 @@
* Represent the version of Car API. This is only updated when there is API change.
* 1 : N
* 2 : O
+ * 3 : O-MR1
*/
- public static final int VERSION = 2;
+ public static final int VERSION = 3;
/** Service name for {@link CarSensorManager}, to be used in {@link #getCarManager(String)}. */
public static final String SENSOR_SERVICE = "sensor";
@@ -79,11 +81,17 @@
/** Service name for {@link CarAudioManager} */
public static final String AUDIO_SERVICE = "audio";
+
/**
* Service name for {@link CarNavigationStatusManager}
* @hide
*/
public static final String CAR_NAVIGATION_SERVICE = "car_navigation_service";
+ /**
+ * Service name for {@link CarInstrumentClusterManager}
+ * @hide
+ */
+ public static final String CAR_INSTRUMENT_CLUSTER_SERVICE = "cluster_service";
/**
* @hide
@@ -94,6 +102,7 @@
/**
* @hide
*/
+ @SystemApi
public static final String DIAGNOSTIC_SERVICE = "diagnostic";
/**
@@ -121,6 +130,11 @@
public static final String VENDOR_EXTENSION_SERVICE = "vendor_extension";
/**
+ * @hide
+ */
+ public static final String BLUETOOTH_SERVICE = "car_bluetooth";
+
+ /**
* @FutureFeature Cannot drop due to usage in non-flag protected place.
* @hide
*/
@@ -143,6 +157,10 @@
/** Permission necessary to access car's speed. */
public static final String PERMISSION_SPEED = "android.car.permission.CAR_SPEED";
+ /** Permission necessary to access car's dynamics state. */
+ public static final String PERMISSION_VEHICLE_DYNAMICS_STATE =
+ "android.car.permission.VEHICLE_DYNAMICS_STATE";
+
/**
* Permission necessary to change car audio volume through {@link CarAudioManager}.
*/
@@ -164,6 +182,24 @@
"android.car.permission.CAR_NAVIGATION_MANAGER";
/**
+ * Permission necessary to start activities in the instrument cluster through
+ * {@link CarInstrumentClusterManager}
+ *
+ * @hide
+ */
+ public static final String PERMISSION_CAR_INSTRUMENT_CLUSTER_CONTROL =
+ "android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL";
+
+ /**
+ * Application must have this permission in order to be launched in the instrument cluster
+ * display.
+ *
+ * @hide
+ */
+ public static final String PERMISSION_CAR_DISPLAY_IN_CLUSTER =
+ "android.car.permission.CAR_DISPLAY_IN_CLUSTER";
+
+ /**
* Permission necessary to access car specific communication channel.
* @hide
*/
@@ -241,19 +277,20 @@
public static final String PERMISSION_VMS_SUBSCRIBER = "android.car.permission.VMS_SUBSCRIBER";
/**
- * Permissions necessary to read diagnostic information.
+ * Permissions necessary to read diagnostic information, including vendor-specific bits.
*
* @hide
*/
- @FutureFeature
- public static final String PERMISSION_CAR_DIAGNOSTIC_READ = "android.car.permission.DIAGNOSTIC_READ";
+ @SystemApi
+ public static final String PERMISSION_CAR_DIAGNOSTIC_READ_ALL =
+ "android.car.permission.DIAGNOSTIC_READ_ALL";
/**
* Permissions necessary to clear diagnostic information.
*
* @hide
*/
- @FutureFeature
+ @SystemApi
public static final String PERMISSION_CAR_DIAGNOSTIC_CLEAR = "android.car.permission.DIAGNOSTIC_CLEAR";
/** Type of car connection: platform runs directly in car. */
@@ -273,6 +310,22 @@
*/
public static final String CAR_NOT_CONNECTED_EXCEPTION_MSG = "CarNotConnected";
+ /**
+ * Activity Action: Provide media playing through a media template app.
+ * <p>Input: String extra mapped by {@link android.app.SearchManager#QUERY} is the query
+ * used to start the media. String extra mapped by {@link #CAR_EXTRA_MEDIA_PACKAGE} is the
+ * package name of the media app which user wants to play media on.
+ * <p>Output: nothing.
+ */
+ public static final String CAR_INTENT_ACTION_MEDIA_TEMPLATE =
+ "android.car.intent.action.MEDIA_TEMPLATE";
+
+ /**
+ * Used as a string extra field with {@link #CAR_INTENT_ACTION_MEDIA_TEMPLATE} to specify the
+ * media app that user wants to start the media on. Note: this is not the templated media app.
+ */
+ public static final String CAR_EXTRA_MEDIA_PACKAGE = "android.car.intent.extra.MEDIA_PACKAGE";
+
/** @hide */
public static final String CAR_SERVICE_INTERFACE_NAME = "android.car.ICar";
@@ -593,6 +646,9 @@
case VENDOR_EXTENSION_SERVICE:
manager = new CarVendorExtensionManager(binder, mEventHandler);
break;
+ case CAR_INSTRUMENT_CLUSTER_SERVICE:
+ manager = new CarInstrumentClusterManager(binder, mEventHandler);
+ break;
case TEST_SERVICE:
/* CarTestManager exist in static library. So instead of constructing it here,
* only pass binder wrapper so that CarTestManager can be constructed outside. */
@@ -603,6 +659,8 @@
manager = new VmsSubscriberManager(binder, mEventHandler);
}
break;
+ case BLUETOOTH_SERVICE:
+ manager = new CarBluetoothManager(binder, mContext);
}
return manager;
}
diff --git a/car-lib/src/android/car/CarBluetoothManager.java b/car-lib/src/android/car/CarBluetoothManager.java
new file mode 100644
index 0000000..841a055
--- /dev/null
+++ b/car-lib/src/android/car/CarBluetoothManager.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car;
+
+import android.Manifest;
+import android.annotation.IntDef;
+import android.annotation.RequiresPermission;
+import android.bluetooth.BluetoothDevice;
+import android.car.CarLibLog;
+import android.car.CarManagerBase;
+import android.car.CarNotConnectedException;
+import android.car.ICarBluetooth;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * APIs for setting Car specific Bluetooth Connection Management policy
+ *
+ * @hide
+ */
+public final class CarBluetoothManager implements CarManagerBase {
+ private static final String TAG = "CarBluetoothManager";
+ private final Context mContext;
+ private final ICarBluetooth mService;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0,
+ BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1})
+ public @interface PriorityType {
+ }
+
+ public static final int BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0 = 0;
+ public static final int BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1 = 1;
+ // Write an empty string to clear a Primary or Secondary device.
+ public static final String BLUETOOTH_NO_PRIORITY_DEVICE = "";
+
+ /**
+ * Set the Auto Connect priority for a paired Bluetooth Device.
+ * For example, if a device is tagged as a Primary device (Priority 0) for a supported
+ * Bluetooth profile, every new Auto Connect attempt would start with trying to connect to
+ * *that* device. This priority is set at a Bluetooth profile granularity.
+ *
+ * @param deviceToSet - Device to set priority (Tag)
+ * @param profileToSet - BluetoothProfile to set priority for
+ * @param priorityToSet - What priority level to set to
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ public void setBluetoothDeviceConnectionPriority(BluetoothDevice deviceToSet, int profileToSet,
+ @PriorityType int priorityToSet) throws CarNotConnectedException {
+ try {
+ mService.setBluetoothDeviceConnectionPriority(deviceToSet, profileToSet, priorityToSet);
+ } catch (RemoteException e) {
+ Log.e(CarLibLog.TAG_CAR, "setBluetoothDeviceConnectionPriority failed", e);
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ * Unset the Auto Connect priority for the given profile
+ *
+ * @param profileToClear - Profile to unset priority
+ * @param priorityToClear - Which priority to clear (Primary or Secondary)
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ public void clearBluetoothDeviceConnectionPriority(int profileToClear,
+ @PriorityType int priorityToClear) throws CarNotConnectedException {
+ try {
+ mService.clearBluetoothDeviceConnectionPriority(profileToClear, priorityToClear);
+ } catch (RemoteException e) {
+ Log.e(CarLibLog.TAG_CAR, "clearBluetoothDeviceConnectionPriority failed", e);
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ * Returns if there is a device that has been tagged with the given priority for the given
+ * profile.
+ *
+ * @param profile - BluetoothProfile
+ * @param priorityToCheck - Priority to check
+ * @return true if there is a device present with the given priority, false if not
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ public boolean isPriorityDevicePresent(int profile, @PriorityType int priorityToCheck)
+ throws CarNotConnectedException {
+ try {
+ return mService.isPriorityDevicePresent(profile, priorityToCheck);
+ } catch (RemoteException e) {
+ Log.e(CarLibLog.TAG_CAR, "isPrioritySet failed", e);
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ * Returns the Bluetooth device address as a String that has been tagged with the given priority
+ * for the given profile.
+ *
+ * @param profile - BluetoothProfile
+ * @param priorityToCheck - Priority to check
+ * @return BluetoothDevice address if present, null if absent
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ public String getDeviceNameWithPriority(int profile, @PriorityType int priorityToCheck)
+ throws CarNotConnectedException {
+ try {
+ return mService.getDeviceNameWithPriority(profile, priorityToCheck);
+ } catch (RemoteException e) {
+ Log.e(CarLibLog.TAG_CAR, "getDeviceNameWithPriority failed", e);
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /** @hide */
+ public CarBluetoothManager(IBinder service, Context context) {
+ mContext = context;
+ mService = ICarBluetooth.Stub.asInterface(service);
+ }
+
+ @Override
+ public void onCarDisconnected() {
+ }
+}
diff --git a/car-lib/src/android/car/CarProjectionManager.java b/car-lib/src/android/car/CarProjectionManager.java
index 1e40fcd..d1824f6 100644
--- a/car-lib/src/android/car/CarProjectionManager.java
+++ b/car-lib/src/android/car/CarProjectionManager.java
@@ -69,13 +69,23 @@
}
/**
+ * Compatibility with previous APIs due to typo
+ * @throws CarNotConnectedException if the connection to the car service has been lost.
+ * @hide
+ */
+ public void regsiterProjectionListener(CarProjectionListener listener, int voiceSearchFilter)
+ throws CarNotConnectedException {
+ registerProjectionListener(listener, voiceSearchFilter);
+ }
+
+ /**
* Register listener to monitor projection. Only one listener can be registered and
* registering multiple times will lead into only the last listener to be active.
* @param listener
* @param voiceSearchFilter Flags of voice search requests to get notification.
* @throws CarNotConnectedException if the connection to the car service has been lost.
*/
- public void regsiterProjectionListener(CarProjectionListener listener, int voiceSearchFilter)
+ public void registerProjectionListener(CarProjectionListener listener, int voiceSearchFilter)
throws CarNotConnectedException {
if (listener == null) {
throw new IllegalArgumentException("null listener");
@@ -83,7 +93,7 @@
synchronized (this) {
if (mListener == null || mVoiceSearchFilter != voiceSearchFilter) {
try {
- mService.regsiterProjectionListener(mBinderListener, voiceSearchFilter);
+ mService.registerProjectionListener(mBinderListener, voiceSearchFilter);
} catch (RemoteException e) {
throw new CarNotConnectedException(e);
}
@@ -94,13 +104,22 @@
}
/**
+ * Compatibility with previous APIs due to typo
+ * @throws CarNotConnectedException if the connection to the car service has been lost.
+ * @hide
+ */
+ public void unregsiterProjectionListener() {
+ unregisterProjectionListener();
+ }
+
+ /**
* Unregister listener and stop listening projection events.
* @throws CarNotConnectedException if the connection to the car service has been lost.
*/
- public void unregsiterProjectionListener() {
+ public void unregisterProjectionListener() {
synchronized (this) {
try {
- mService.unregsiterProjectionListener(mBinderListener);
+ mService.unregisterProjectionListener(mBinderListener);
} catch (RemoteException e) {
//ignore
}
diff --git a/car-lib/src/android/car/ICarBluetooth.aidl b/car-lib/src/android/car/ICarBluetooth.aidl
new file mode 100644
index 0000000..a5fbb73
--- /dev/null
+++ b/car-lib/src/android/car/ICarBluetooth.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car;
+import android.bluetooth.BluetoothDevice;
+
+/** @hide */
+interface ICarBluetooth {
+ void setBluetoothDeviceConnectionPriority(in BluetoothDevice deviceToSet, in int profileToSet,
+ in int priorityToSet);
+ void clearBluetoothDeviceConnectionPriority(in int profileToClear,in int priorityToClear);
+ boolean isPriorityDevicePresent(in int profile, in int priorityToCheck);
+ String getDeviceNameWithPriority(in int profile, in int priorityToCheck);
+}
diff --git a/car-lib/src/android/car/ICarProjection.aidl b/car-lib/src/android/car/ICarProjection.aidl
index 96eb2a9..15831cd 100644
--- a/car-lib/src/android/car/ICarProjection.aidl
+++ b/car-lib/src/android/car/ICarProjection.aidl
@@ -42,10 +42,10 @@
* Registers projection callback.
* Re-registering same callback with different filter will cause only filter to update.
*/
- void regsiterProjectionListener(ICarProjectionCallback callback, int filter) = 2;
+ void registerProjectionListener(ICarProjectionCallback callback, int filter) = 2;
/**
* Unregisters projection callback.
*/
- void unregsiterProjectionListener(ICarProjectionCallback callback) = 3;
+ void unregisterProjectionListener(ICarProjectionCallback callback) = 3;
}
diff --git a/car-lib/src/android/car/cluster/CarInstrumentClusterManager.java b/car-lib/src/android/car/cluster/CarInstrumentClusterManager.java
new file mode 100644
index 0000000..758ce9e
--- /dev/null
+++ b/car-lib/src/android/car/cluster/CarInstrumentClusterManager.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.cluster;
+
+import android.car.CarManagerBase;
+import android.car.CarNotConnectedException;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * API to work with instrument cluster.
+ *
+ * @hide
+ */
+public class CarInstrumentClusterManager implements CarManagerBase {
+ private static final String TAG = CarInstrumentClusterManager.class.getSimpleName();
+
+ /** @hide */
+ public static final String CATEGORY_NAVIGATION = "android.car.cluster.NAVIGATION";
+
+ /**
+ * When activity in the cluster is launched it will receive {@link ClusterActivityState} in the
+ * intent's extra thus activity will know information about unobscured area, etc. upon activity
+ * creation.
+ *
+ * @hide
+ */
+ public static final String KEY_EXTRA_ACTIVITY_STATE =
+ "android.car.cluster.ClusterActivityState";
+
+ private final EventHandler mHandler;
+ private final Map<String, Set<Callback>> mCallbacksByCategory = new HashMap<>(0);
+ private final Object mLock = new Object();
+ private final Map<String, Bundle> mActivityStatesByCategory = new HashMap<>(0);
+
+ private final IInstrumentClusterManagerService mService;
+
+ private ClusterManagerCallback mServiceToManagerCallback;
+
+ /**
+ * Starts activity in the instrument cluster.
+ *
+ * @hide
+ */
+ public void startActivity(Intent intent) throws CarNotConnectedException {
+ try {
+ mService.startClusterActivity(intent);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ * Caller of this method will receive immediate callback with the most recent state if state
+ * exists for given category.
+ *
+ * @param category category of the activity in the cluster,
+ * see {@link #CATEGORY_NAVIGATION}
+ * @param callback instance of {@link Callback} class to receive events.
+ *
+ * @hide
+ */
+ public void registerCallback(String category, Callback callback)
+ throws CarNotConnectedException {
+ Log.i(TAG, "registerCallback, category: " + category + ", callback: " + callback);
+ ClusterManagerCallback callbackToCarService = null;
+ synchronized (mLock) {
+ Set<Callback> callbacks = mCallbacksByCategory.get(category);
+ if (callbacks == null) {
+ callbacks = new HashSet<>(1);
+ mCallbacksByCategory.put(category, callbacks);
+ }
+ if (!callbacks.add(callback)) {
+ Log.w(TAG, "registerCallback: already registered");
+ return; // already registered
+ }
+
+ if (mActivityStatesByCategory.containsKey(category)) {
+ Log.i(TAG, "registerCallback: sending activity state...");
+ callback.onClusterActivityStateChanged(
+ category, mActivityStatesByCategory.get(category));
+ }
+
+ if (mServiceToManagerCallback == null) {
+ Log.i(TAG, "registerCallback: registering callback with car service...");
+ mServiceToManagerCallback = new ClusterManagerCallback();
+ callbackToCarService = mServiceToManagerCallback;
+ }
+ }
+ try {
+ mService.registerCallback(callbackToCarService);
+ Log.i(TAG, "registerCallback: done");
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ * Unregisters given callback for all activity categories.
+ *
+ * @param callback previously registered callback
+ *
+ * @hide
+ */
+ public void unregisterCallback(Callback callback) throws CarNotConnectedException {
+ List<String> keysToRemove = new ArrayList<>(1);
+ synchronized (mLock) {
+ for (Map.Entry<String, Set<Callback>> entry : mCallbacksByCategory.entrySet()) {
+ Set<Callback> callbacks = entry.getValue();
+ if (callbacks.remove(callback) && callbacks.isEmpty()) {
+ keysToRemove.add(entry.getKey());
+ }
+
+ }
+
+ for (String key: keysToRemove) {
+ mCallbacksByCategory.remove(key);
+ }
+
+ if (mCallbacksByCategory.isEmpty()) {
+ try {
+ mService.unregisterCallback(mServiceToManagerCallback);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ mServiceToManagerCallback = null;
+ }
+ }
+ }
+
+ /** @hide */
+ public CarInstrumentClusterManager(IBinder service, Handler handler) {
+ mService = IInstrumentClusterManagerService.Stub.asInterface(service);
+
+ mHandler = new EventHandler(handler.getLooper());
+ }
+
+ /** @hide */
+ public interface Callback {
+
+ /**
+ * Notify client that activity state was changed.
+ *
+ * @param category cluster activity category, see {@link #CATEGORY_NAVIGATION}
+ * @param clusterActivityState see {@link ClusterActivityState} how to read this bundle.
+ */
+ void onClusterActivityStateChanged(String category, Bundle clusterActivityState);
+ }
+
+ /** @hide */
+ @Override
+ public void onCarDisconnected() {
+ }
+
+ private class EventHandler extends Handler {
+
+ final static int MSG_ACTIVITY_STATE = 1;
+
+ EventHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ Log.i(TAG, "handleMessage, message: " + msg);
+ switch (msg.what) {
+ case MSG_ACTIVITY_STATE:
+ Pair<String, Bundle> info = (Pair<String, Bundle>) msg.obj;
+ String category = info.first;
+ Bundle state = info.second;
+ List<CarInstrumentClusterManager.Callback> callbacks = null;
+ synchronized (mLock) {
+ if (mCallbacksByCategory.containsKey(category)) {
+ callbacks = new ArrayList<>(mCallbacksByCategory.get(category));
+ }
+ }
+ Log.i(TAG, "handleMessage, callbacks: " + callbacks);
+ if (callbacks != null) {
+ for (CarInstrumentClusterManager.Callback cb : callbacks) {
+ cb.onClusterActivityStateChanged(category, state);
+ }
+ }
+ break;
+ default:
+ Log.e(TAG, "Unexpected message: " + msg.what);
+ }
+ }
+ }
+
+ private class ClusterManagerCallback extends IInstrumentClusterManagerCallback.Stub {
+
+ @Override
+ public void setClusterActivityState(String category, Bundle clusterActivityState)
+ throws RemoteException {
+ Log.i(TAG, "setClusterActivityState, category: " + category);
+ synchronized (mLock) {
+ mActivityStatesByCategory.put(category, clusterActivityState);
+ }
+
+ mHandler.sendMessage(mHandler.obtainMessage(EventHandler.MSG_ACTIVITY_STATE,
+ new Pair<>(category, clusterActivityState)));
+ }
+ }
+}
\ No newline at end of file
diff --git a/car-lib/src/android/car/cluster/ClusterActivityState.java b/car-lib/src/android/car/cluster/ClusterActivityState.java
new file mode 100644
index 0000000..9a6223c
--- /dev/null
+++ b/car-lib/src/android/car/cluster/ClusterActivityState.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.cluster;
+
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.os.Bundle;
+
+/**
+ * Helper class that represents activity state in the cluster and can be serialized / deserialized
+ * to/from bundle.
+ * @hide
+ */
+public class ClusterActivityState {
+ private static final String KEY_VISIBLE = "android.car:activityState.visible";
+ private static final String KEY_UNOBSCURED_BOUNDS = "android.car:activityState.unobscured";
+ private static final String KEY_EXTRAS = "android.car:activityState.extras";
+
+ private boolean mVisible = true;
+ private Rect mUnobscuredBounds;
+ private Bundle mExtras;
+
+ public boolean isVisible() {
+ return mVisible;
+ }
+
+ @Nullable public Rect getUnobscuredBounds() {
+ return mUnobscuredBounds;
+ }
+
+ public ClusterActivityState setVisible(boolean visible) {
+ mVisible = visible;
+ return this;
+ }
+
+ public ClusterActivityState setUnobscuredBounds(Rect unobscuredBounds) {
+ mUnobscuredBounds = unobscuredBounds;
+ return this;
+ }
+
+ public ClusterActivityState setExtras(Bundle bundle) {
+ mExtras = bundle;
+ return this;
+ }
+
+ /** Use factory methods instead. */
+ private ClusterActivityState() {}
+
+ public static ClusterActivityState create(boolean visible, Rect unobscuredBounds) {
+ return new ClusterActivityState()
+ .setVisible(visible)
+ .setUnobscuredBounds(unobscuredBounds);
+ }
+
+ public static ClusterActivityState fromBundle(Bundle bundle) {
+ return new ClusterActivityState()
+ .setVisible(bundle.getBoolean(KEY_VISIBLE, true))
+ .setUnobscuredBounds((Rect) bundle.getParcelable(KEY_UNOBSCURED_BOUNDS))
+ .setExtras(bundle.getBundle(KEY_EXTRAS));
+ }
+
+ public Bundle toBundle() {
+ Bundle b = new Bundle();
+ b.putBoolean(KEY_VISIBLE, mVisible);
+ b.putParcelable(KEY_UNOBSCURED_BOUNDS, mUnobscuredBounds);
+ b.putBundle(KEY_EXTRAS, mExtras);
+ return b;
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + " {"
+ + "visible: " + mVisible + ", "
+ + "unobscuredBounds: " + mUnobscuredBounds
+ + " }";
+ }
+}
diff --git a/car-lib/src/android/car/cluster/IInstrumentClusterManagerCallback.aidl b/car-lib/src/android/car/cluster/IInstrumentClusterManagerCallback.aidl
new file mode 100644
index 0000000..91a497d
--- /dev/null
+++ b/car-lib/src/android/car/cluster/IInstrumentClusterManagerCallback.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster;
+
+import android.os.Bundle;
+
+/**
+ * Interface from Car Service to {@link android.car.cluster.CarInstrumentClusterManager}
+ * @hide
+ */
+interface IInstrumentClusterManagerCallback {
+ /**
+ * Notifies manager about changes in the cluster activity state.
+ *
+ * @param category cluster activity category to which this state applies,
+ * see {@link android.car.cluster.CarInstrumentClusterManager} for details.
+ * @param clusterActivityState is a {@link Bundle} object,
+ * see {@link android.car.cluster.ClusterActivityState} for how to construct the bundle.
+ * @hide
+ */
+ oneway void setClusterActivityState(String category, in Bundle clusterActivityState);
+}
diff --git a/car-lib/src/android/car/cluster/IInstrumentClusterManagerService.aidl b/car-lib/src/android/car/cluster/IInstrumentClusterManagerService.aidl
new file mode 100644
index 0000000..aaaeaee
--- /dev/null
+++ b/car-lib/src/android/car/cluster/IInstrumentClusterManagerService.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster;
+
+import android.content.Intent;
+
+import android.car.cluster.IInstrumentClusterManagerCallback;
+
+/**
+ * API to communicate between {@link CarInstrumentClusterManager} and Car Service.
+ *
+ * @hide
+ */
+interface IInstrumentClusterManagerService {
+ oneway void startClusterActivity(in Intent intent);
+
+ oneway void registerCallback(in IInstrumentClusterManagerCallback callback);
+ oneway void unregisterCallback(in IInstrumentClusterManagerCallback callback);
+}
diff --git a/car-lib/src/android/car/cluster/renderer/DisplayConfiguration.java b/car-lib/src/android/car/cluster/renderer/DisplayConfiguration.java
deleted file mode 100644
index 409c817..0000000
--- a/car-lib/src/android/car/cluster/renderer/DisplayConfiguration.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.car.cluster.renderer;
-
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.graphics.Rect;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * TODO: need to properly define this class and make it immutable. bug: 32060601
- *
- * @hide
- */
-@SystemApi
-public class DisplayConfiguration implements Parcelable {
- private final Rect mPrimaryRegion;
-
- @Nullable
- private final Rect mSecondaryRegion;
-
- public static final Parcelable.Creator<DisplayConfiguration> CREATOR =
- new Parcelable.Creator<DisplayConfiguration>() {
-
- public DisplayConfiguration createFromParcel(Parcel in) {
- return new DisplayConfiguration(in);
- }
-
- public DisplayConfiguration[] newArray(int size) {
- return new DisplayConfiguration[size];
- }
- };
-
-
- public DisplayConfiguration(Parcel in) {
- mPrimaryRegion = in.readTypedObject(Rect.CREATOR);
- mSecondaryRegion = in.readTypedObject(Rect.CREATOR);
- }
-
- public DisplayConfiguration(Rect primaryRegion) {
- this(primaryRegion, null);
- }
-
- public DisplayConfiguration(Rect primaryRegion, @Nullable Rect secondaryRegion) {
- mPrimaryRegion = primaryRegion;
- mSecondaryRegion = secondaryRegion;
- }
-
-
- /** Region that will be fully visible in instrument cluster */
- public Rect getPrimaryRegion() {
- return mPrimaryRegion;
- }
-
- /**
- * The region that includes primary region + may include some additional space that might
- * be partially visible in the instrument cluster. It is useful to fade-out primary
- * rendering for example or adding background image.
- */
- @Nullable
- public Rect getSecondaryRegion() {
- return mSecondaryRegion;
- }
-
- /** Returns true if secondary region is strongly greater then primary region */
- public boolean hasSecondaryRegion() {
- return mSecondaryRegion != null
- && mSecondaryRegion.width() > mPrimaryRegion.width()
- && mSecondaryRegion.height() > mPrimaryRegion.height();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeTypedObject(mPrimaryRegion, 0);
- dest.writeTypedObject(mSecondaryRegion, 0);
- }
-}
diff --git a/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl b/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl
index 9fb4b56..3458975 100644
--- a/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl
+++ b/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl
@@ -15,6 +15,7 @@
*/
package android.car.cluster.renderer;
+import android.car.cluster.renderer.IInstrumentClusterCallback;
import android.car.cluster.renderer.IInstrumentClusterNavigation;
import android.view.KeyEvent;
@@ -28,8 +29,8 @@
IInstrumentClusterNavigation getNavigationService();
/** Supplies Instrument Cluster Renderer with current owner of Navigation app context */
- void setNavigationContextOwner(int uid, int pid);
+ oneway void setNavigationContextOwner(int uid, int pid);
/** Called when key event that was addressed to instrument cluster display has been received. */
- void onKeyEvent(in KeyEvent keyEvent);
+ oneway void onKeyEvent(in KeyEvent keyEvent);
}
diff --git a/car-lib/src/android/car/cluster/renderer/IInstrumentClusterCallback.aidl b/car-lib/src/android/car/cluster/renderer/IInstrumentClusterCallback.aidl
new file mode 100644
index 0000000..996dc9e
--- /dev/null
+++ b/car-lib/src/android/car/cluster/renderer/IInstrumentClusterCallback.aidl
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster.renderer;
+
+import android.graphics.Rect;
+import android.os.Bundle;
+
+/**
+ * This interface defines the communication channel between the cluster vendor implementation and
+ * Car Service.
+ *
+ * @hide
+ */
+interface IInstrumentClusterCallback {
+ /**
+ * Notify Car Service how to launch an activity for particular category.
+ *
+ * @param category cluster activity category,
+ * see {@link android.car.cluster.CarInstrumentClusterManager} for details.
+ * @param activityOptions this bundle will be converted to {@link android.app.ActivityOptions}
+ * and used when starting an activity. It may contain information such as virtual display
+ * id or activity stack id where to start cluster activity.
+ *
+ * @hide
+ */
+ void setClusterActivityLaunchOptions(String category, in Bundle activityOptions);
+
+ /**
+ * Activities launched on virtual display will be in onPause state most of the time, so they
+ * can't really know whether they visible on the screen or not. We need to propagate this
+ * information along with unobscured bounds (and possible other info) from instrument cluster
+ * vendor implementation to activity.
+ *
+ * @param category cluster activity category to which this state applies,
+ * see {@link android.car.cluster.CarInstrumentClusterManager} for details.
+ * @param clusterActivityState is a {@link Bundle} object,
+ * see {@link android.car.cluster.ClusterActivityState} for how to construct the bundle.
+ * @hide
+ */
+ void setClusterActivityState(String category, in Bundle clusterActivityState);
+}
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..e34f2fa 100644
--- a/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
+++ b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
@@ -18,11 +18,14 @@
import android.annotation.CallSuper;
import android.annotation.MainThread;
import android.annotation.SystemApi;
+import android.app.ActivityOptions;
import android.app.Service;
import android.car.CarLibLog;
+import android.car.CarNotConnectedException;
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;
@@ -32,6 +35,8 @@
import android.util.Pair;
import android.view.KeyEvent;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -59,6 +64,14 @@
private RendererBinder mRendererBinder;
+ /** @hide */
+ public static final String EXTRA_KEY_CALLBACK_SERVICE =
+ "android.car.cluster.IInstrumentClusterCallback";
+
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private IInstrumentClusterCallback mCallback;
+
@Override
@CallSuper
public IBinder onBind(Intent intent) {
@@ -66,6 +79,15 @@
Log.d(TAG, "onBind, intent: " + intent);
}
+ if (intent.getExtras().containsKey(EXTRA_KEY_CALLBACK_SERVICE)) {
+ IBinder callbackBinder = intent.getExtras().getBinder(EXTRA_KEY_CALLBACK_SERVICE);
+ synchronized (mLock) {
+ mCallback = IInstrumentClusterCallback.Stub.asInterface(callbackBinder);
+ }
+ } else {
+ Log.w(TAG, "onBind, no callback in extra!");
+ }
+
if (mRendererBinder == null) {
mRendererBinder = new RendererBinder(getNavigationRenderer());
}
@@ -82,6 +104,57 @@
protected void onKeyEvent(KeyEvent keyEvent) {
}
+ /**
+ *
+ * Sets configuration for activities that should be launched directly in the instrument
+ * cluster.
+ *
+ * @param category category of cluster activity
+ * @param activityOptions contains information of how to start cluster activity (on what display
+ * or activity stack.
+ *
+ * @hide
+ */
+ public void setClusterActivityLaunchOptions(String category,
+ ActivityOptions activityOptions) throws CarNotConnectedException {
+ IInstrumentClusterCallback cb;
+ synchronized (mLock) {
+ cb = mCallback;
+ }
+ if (cb == null) throw new CarNotConnectedException();
+ try {
+ cb.setClusterActivityLaunchOptions(category, activityOptions.toBundle());
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ *
+ * @param category cluster activity category,
+ * see {@link android.car.cluster.CarInstrumentClusterManager}
+ * @param state pass information about activity state,
+ * see {@link android.car.cluster.ClusterActivityState}
+ * @return true if information was sent to Car Service
+ * @throws CarNotConnectedException
+ *
+ * @hide
+ */
+ public void setClusterActivityState(String category, Bundle state)
+ throws CarNotConnectedException {
+ IInstrumentClusterCallback cb;
+ synchronized (mLock) {
+ cb = mCallback;
+ }
+ if (cb == null) throw new CarNotConnectedException();
+ try {
+ cb.setClusterActivityState(category, state);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+
@Override
protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
writer.println("**" + getClass().getSimpleName() + "**");
@@ -89,12 +162,19 @@
if (mRendererBinder != null) {
writer.println("navigation renderer: " + mRendererBinder.mNavigationRenderer);
String owner = "none";
- if (mRendererBinder.mNavContextOwner != null) {
- owner = "[uid: " + mRendererBinder.mNavContextOwner.first
- + ", pid: " + mRendererBinder.mNavContextOwner.second + "]";
+ synchronized (mLock) {
+ if (mRendererBinder.mNavContextOwner != null) {
+ owner = "[uid: " + mRendererBinder.mNavContextOwner.first
+ + ", pid: " + mRendererBinder.mNavContextOwner.second + "]";
+ }
}
writer.println("navigation focus owner: " + owner);
}
+ IInstrumentClusterCallback cb;
+ synchronized (mLock) {
+ cb = mCallback;
+ }
+ writer.println("callback: " + cb);
}
private class RendererBinder extends IInstrumentCluster.Stub {
@@ -102,8 +182,10 @@
private final NavigationRenderer mNavigationRenderer;
private final UiHandler mUiHandler;
- private volatile NavigationBinder mNavigationBinder;
- private volatile Pair<Integer, Integer> mNavContextOwner;
+ @GuardedBy("mLock")
+ private NavigationBinder mNavigationBinder;
+ @GuardedBy("mLock")
+ private Pair<Integer, Integer> mNavContextOwner;
RendererBinder(NavigationRenderer navigationRenderer) {
mNavigationRenderer = navigationRenderer;
@@ -112,21 +194,25 @@
@Override
public IInstrumentClusterNavigation getNavigationService() throws RemoteException {
- if (mNavigationBinder == null) {
- mNavigationBinder = new NavigationBinder(mNavigationRenderer);
- if (mNavContextOwner != null) {
- mNavigationBinder.setNavigationContextOwner(
- mNavContextOwner.first, mNavContextOwner.second);
+ synchronized (mLock) {
+ if (mNavigationBinder == null) {
+ mNavigationBinder = new NavigationBinder(mNavigationRenderer);
+ if (mNavContextOwner != null) {
+ mNavigationBinder.setNavigationContextOwner(
+ mNavContextOwner.first, mNavContextOwner.second);
+ }
}
+ return mNavigationBinder;
}
- return mNavigationBinder;
}
@Override
public void setNavigationContextOwner(int uid, int pid) throws RemoteException {
- mNavContextOwner = new Pair<>(uid, pid);
- if (mNavigationBinder != null) {
- mNavigationBinder.setNavigationContextOwner(uid, pid);
+ synchronized (mLock) {
+ mNavContextOwner = new Pair<>(uid, pid);
+ if (mNavigationBinder != null) {
+ mNavigationBinder.setNavigationContextOwner(uid, pid);
+ }
}
}
@@ -181,6 +267,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/hardware/CarDiagnosticEvent.aidl b/car-lib/src/android/car/diagnostic/CarDiagnosticEvent.aidl
similarity index 95%
copy from car-lib/src/android/car/hardware/CarDiagnosticEvent.aidl
copy to car-lib/src/android/car/diagnostic/CarDiagnosticEvent.aidl
index 73b184e..cd9b9a7 100644
--- a/car-lib/src/android/car/hardware/CarDiagnosticEvent.aidl
+++ b/car-lib/src/android/car/diagnostic/CarDiagnosticEvent.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.car.hardware;
+package android.car.diagnostic;
parcelable CarDiagnosticEvent;
diff --git a/car-lib/src/android/car/diagnostic/CarDiagnosticEvent.java b/car-lib/src/android/car/diagnostic/CarDiagnosticEvent.java
new file mode 100644
index 0000000..e8ec2b5
--- /dev/null
+++ b/car-lib/src/android/car/diagnostic/CarDiagnosticEvent.java
@@ -0,0 +1,885 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.diagnostic;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.JsonWriter;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * A CarDiagnosticEvent object corresponds to a single diagnostic event frame coming from the car.
+ *
+ * @hide
+ */
+@SystemApi
+public class CarDiagnosticEvent implements Parcelable {
+ /** Whether this frame represents a live or a freeze frame */
+ public final int frameType;
+
+ /**
+ * When this data was acquired in car or received from car. It is elapsed real-time of data
+ * reception from car in nanoseconds since system boot.
+ */
+ public final long timestamp;
+
+ /**
+ * Sparse array that contains the mapping of OBD2 diagnostic properties to their values for
+ * integer valued properties
+ */
+ private final SparseIntArray intValues;
+
+ /**
+ * Sparse array that contains the mapping of OBD2 diagnostic properties to their values for
+ * float valued properties
+ */
+ private final SparseArray<Float> floatValues;
+
+ /**
+ * Diagnostic Troubleshooting Code (DTC) that was detected and caused this frame to be stored
+ * (if a freeze frame). Always null for a live frame.
+ */
+ public final String dtc;
+
+ public CarDiagnosticEvent(Parcel in) {
+ frameType = in.readInt();
+ timestamp = in.readLong();
+ int len = in.readInt();
+ floatValues = new SparseArray<>(len);
+ for (int i = 0; i < len; ++i) {
+ int key = in.readInt();
+ float value = in.readFloat();
+ floatValues.put(key, value);
+ }
+ len = in.readInt();
+ intValues = new SparseIntArray(len);
+ for (int i = 0; i < len; ++i) {
+ int key = in.readInt();
+ int value = in.readInt();
+ intValues.put(key, value);
+ }
+ dtc = (String) in.readValue(String.class.getClassLoader());
+ // version 1 up to here
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(frameType);
+ dest.writeLong(timestamp);
+ dest.writeInt(floatValues.size());
+ for (int i = 0; i < floatValues.size(); ++i) {
+ int key = floatValues.keyAt(i);
+ dest.writeInt(key);
+ dest.writeFloat(floatValues.get(key));
+ }
+ dest.writeInt(intValues.size());
+ for (int i = 0; i < intValues.size(); ++i) {
+ int key = intValues.keyAt(i);
+ dest.writeInt(key);
+ dest.writeInt(intValues.get(key));
+ }
+ dest.writeValue(dtc);
+ }
+
+ /**
+ * Store the contents of this diagnostic event in a JsonWriter.
+ *
+ * The data is stored as a JSON object, with these fields:
+ * type: either "live" or "freeze" depending on the type of frame;
+ * timestamp: the timestamp at which this frame was generated;
+ * intValues: an array of objects each of which has two elements:
+ * id: the integer identifier of the sensor;
+ * value: the integer value of the sensor;
+ * floatValues: an array of objects each of which has two elements:
+ * id: the integer identifier of the sensor;
+ * value: the floating-point value of the sensor;
+ * stringValue: the DTC for a freeze frame, omitted for a live frame
+ */
+ public void writeToJson(JsonWriter jsonWriter) throws IOException {
+ jsonWriter.beginObject();
+
+ jsonWriter.name("type");
+ switch (frameType) {
+ case CarDiagnosticManager.FRAME_TYPE_LIVE:
+ jsonWriter.value("live");
+ break;
+ case CarDiagnosticManager.FRAME_TYPE_FREEZE:
+ jsonWriter.value("freeze");
+ break;
+ default:
+ throw new IllegalStateException("unknown frameType " + frameType);
+ }
+
+ jsonWriter.name("timestamp").value(timestamp);
+
+ jsonWriter.name("intValues").beginArray();
+ for (int i = 0; i < intValues.size(); ++i) {
+ jsonWriter.beginObject();
+ jsonWriter.name("id").value(intValues.keyAt(i));
+ jsonWriter.name("value").value(intValues.valueAt(i));
+ jsonWriter.endObject();
+ }
+ jsonWriter.endArray();
+
+ jsonWriter.name("floatValues").beginArray();
+ for (int i = 0; i < floatValues.size(); ++i) {
+ jsonWriter.beginObject();
+ jsonWriter.name("id").value(floatValues.keyAt(i));
+ jsonWriter.name("value").value(floatValues.valueAt(i));
+ jsonWriter.endObject();
+ }
+ jsonWriter.endArray();
+
+ if (dtc != null) {
+ jsonWriter.name("stringValue").value(dtc);
+ }
+
+ jsonWriter.endObject();
+ }
+
+ public static final Parcelable.Creator<CarDiagnosticEvent> CREATOR =
+ new Parcelable.Creator<CarDiagnosticEvent>() {
+ public CarDiagnosticEvent createFromParcel(Parcel in) {
+ return new CarDiagnosticEvent(in);
+ }
+
+ public CarDiagnosticEvent[] newArray(int size) {
+ return new CarDiagnosticEvent[size];
+ }
+ };
+
+ private CarDiagnosticEvent(
+ int frameType,
+ long timestamp,
+ SparseArray<Float> floatValues,
+ SparseIntArray intValues,
+ String dtc) {
+ this.frameType = frameType;
+ this.timestamp = timestamp;
+ this.floatValues = floatValues;
+ this.intValues = intValues;
+ this.dtc = dtc;
+ }
+
+ /**
+ * This class can be used to incrementally construct a CarDiagnosticEvent.
+ * CarDiagnosticEvent instances are immutable once built.
+ */
+ public static class Builder {
+ private int mType = CarDiagnosticManager.FRAME_TYPE_LIVE;
+ private long mTimestamp = 0;
+ private SparseArray<Float> mFloatValues = new SparseArray<>();
+ private SparseIntArray mIntValues = new SparseIntArray();
+ private String mDtc = null;
+
+ private Builder(int type) {
+ mType = type;
+ }
+
+ /** Returns a new Builder for a live frame */
+ public static Builder newLiveFrameBuilder() {
+ return new Builder(CarDiagnosticManager.FRAME_TYPE_LIVE);
+ }
+
+ /** Returns a new Builder for a freeze frame */
+ public static Builder newFreezeFrameBuilder() {
+ return new Builder(CarDiagnosticManager.FRAME_TYPE_FREEZE);
+ }
+
+ /** Sets the timestamp for the frame being built */
+ public Builder atTimestamp(long timestamp) {
+ mTimestamp = timestamp;
+ return this;
+ }
+
+ /** Adds an integer-valued sensor to the frame being built */
+ public Builder withIntValue(int key, int value) {
+ mIntValues.put(key, value);
+ return this;
+ }
+
+ /** Adds a float-valued sensor to the frame being built */
+ public Builder withFloatValue(int key, float value) {
+ mFloatValues.put(key, value);
+ return this;
+ }
+
+ /** Sets the DTC for the frame being built */
+ public Builder withDtc(String dtc) {
+ mDtc = dtc;
+ return this;
+ }
+
+ /** Builds and returns the CarDiagnosticEvent */
+ public CarDiagnosticEvent build() {
+ return new CarDiagnosticEvent(mType, mTimestamp, mFloatValues, mIntValues, mDtc);
+ }
+ }
+
+ /**
+ * Returns a copy of this CarDiagnosticEvent with all vendor-specific sensors removed.
+ *
+ * @hide
+ */
+ public CarDiagnosticEvent withVendorSensorsRemoved() {
+ SparseIntArray newIntValues = intValues.clone();
+ SparseArray<Float> newFloatValues = floatValues.clone();
+ for (int i = 0; i < intValues.size(); ++i) {
+ int key = intValues.keyAt(i);
+ if (key >= android.car.diagnostic.IntegerSensorIndex.LAST_SYSTEM) {
+ newIntValues.delete(key);
+ }
+ }
+ for (int i = 0; i < floatValues.size(); ++i) {
+ int key = floatValues.keyAt(i);
+ if (key >= android.car.diagnostic.FloatSensorIndex.LAST_SYSTEM) {
+ newFloatValues.delete(key);
+ }
+ }
+ return new CarDiagnosticEvent(frameType, timestamp, newFloatValues, newIntValues, dtc);
+ }
+
+ /** Returns true if this object is a live frame, false otherwise */
+ public boolean isLiveFrame() {
+ return CarDiagnosticManager.FRAME_TYPE_LIVE == frameType;
+ }
+
+ /** Returns true if this object is a freeze frame, false otherwise */
+ public boolean isFreezeFrame() {
+ return CarDiagnosticManager.FRAME_TYPE_FREEZE == frameType;
+ }
+
+ /** @hide */
+ public boolean isEmptyFrame() {
+ boolean empty = (0 == intValues.size());
+ empty &= (0 == floatValues.size());
+ if (isFreezeFrame()) empty &= dtc.isEmpty();
+ return empty;
+ }
+
+ /** @hide */
+ public CarDiagnosticEvent checkLiveFrame() {
+ if (!isLiveFrame()) throw new IllegalStateException("frame is not a live frame");
+ return this;
+ }
+
+ /** @hide */
+ public CarDiagnosticEvent checkFreezeFrame() {
+ if (!isFreezeFrame()) throw new IllegalStateException("frame is not a freeze frame");
+ return this;
+ }
+
+ /** @hide */
+ public boolean isEarlierThan(CarDiagnosticEvent otherEvent) {
+ otherEvent = Objects.requireNonNull(otherEvent);
+ return (timestamp < otherEvent.timestamp);
+ }
+
+ @Override
+ public boolean equals(Object otherObject) {
+ if (this == otherObject) {
+ return true;
+ }
+ if (null == otherObject) {
+ return false;
+ }
+ if (!(otherObject instanceof CarDiagnosticEvent)) {
+ return false;
+ }
+ CarDiagnosticEvent otherEvent = (CarDiagnosticEvent)otherObject;
+ if (otherEvent.frameType != frameType)
+ return false;
+ if (otherEvent.timestamp != timestamp)
+ return false;
+ if (otherEvent.intValues.size() != intValues.size())
+ return false;
+ if (otherEvent.floatValues.size() != floatValues.size())
+ return false;
+ if (!Objects.equals(dtc, otherEvent.dtc))
+ return false;
+ for (int i = 0; i < intValues.size(); ++i) {
+ int key = intValues.keyAt(i);
+ int otherKey = otherEvent.intValues.keyAt(i);
+ if (key != otherKey) {
+ return false;
+ }
+ int value = intValues.valueAt(i);
+ int otherValue = otherEvent.intValues.valueAt(i);
+ if (value != otherValue) {
+ return false;
+ }
+ }
+ for (int i = 0; i < floatValues.size(); ++i) {
+ int key = floatValues.keyAt(i);
+ int otherKey = otherEvent.floatValues.keyAt(i);
+ if (key != otherKey) {
+ return false;
+ }
+ float value = floatValues.valueAt(i);
+ float otherValue = otherEvent.floatValues.valueAt(i);
+ if (value != otherValue) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ Integer[] intKeys = new Integer[intValues.size()];
+ Integer[] floatKeys = new Integer[floatValues.size()];
+ Integer[] intValues = new Integer[intKeys.length];
+ Float[] floatValues = new Float[floatKeys.length];
+ for (int i = 0; i < intKeys.length; ++i) {
+ intKeys[i] = this.intValues.keyAt(i);
+ intValues[i] = this.intValues.valueAt(i);
+ }
+ for (int i = 0; i < floatKeys.length; ++i) {
+ floatKeys[i] = this.floatValues.keyAt(i);
+ floatValues[i] = this.floatValues.valueAt(i);
+ }
+ int intKeysHash = Objects.hash((Object[])intKeys);
+ int intValuesHash = Objects.hash((Object[])intValues);
+ int floatKeysHash = Objects.hash((Object[])floatKeys);
+ int floatValuesHash = Objects.hash((Object[])floatValues);
+ return Objects.hash(frameType,
+ timestamp,
+ dtc,
+ intKeysHash,
+ intValuesHash,
+ floatKeysHash,
+ floatValuesHash);
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "%s diagnostic frame {\n"
+ + "\ttimestamp: %d, "
+ + "DTC: %s\n"
+ + "\tintValues: %s\n"
+ + "\tfloatValues: %s\n}",
+ isLiveFrame() ? "live" : "freeze",
+ timestamp,
+ dtc,
+ intValues.toString(),
+ floatValues.toString());
+ }
+
+ /**
+ * Returns the value of the given integer sensor, if present in this frame.
+ * Returns defaultValue otherwise.
+ */
+ public int getSystemIntegerSensor(
+ @android.car.diagnostic.IntegerSensorIndex.SensorIndex int sensor, int defaultValue) {
+ return intValues.get(sensor, defaultValue);
+ }
+
+ /**
+ * Returns the value of the given float sensor, if present in this frame.
+ * Returns defaultValue otherwise.
+ */
+ public float getSystemFloatSensor(
+ @android.car.diagnostic.FloatSensorIndex.SensorIndex int sensor, float defaultValue) {
+ return floatValues.get(sensor, defaultValue);
+ }
+
+ /**
+ * Returns the value of the given integer sensor, if present in this frame.
+ * Returns defaultValue otherwise.
+ */
+ public int getVendorIntegerSensor(int sensor, int defaultValue) {
+ return intValues.get(sensor, defaultValue);
+ }
+
+ /**
+ * Returns the value of the given float sensor, if present in this frame.
+ * Returns defaultValue otherwise.
+ */
+ public float getVendorFloatSensor(int sensor, float defaultValue) {
+ return floatValues.get(sensor, defaultValue);
+ }
+
+ /**
+ * Returns the value of the given integer sensor, if present in this frame.
+ * Returns null otherwise.
+ */
+ public @Nullable Integer getSystemIntegerSensor(
+ @android.car.diagnostic.IntegerSensorIndex.SensorIndex int sensor) {
+ int index = intValues.indexOfKey(sensor);
+ if (index < 0) return null;
+ return intValues.valueAt(index);
+ }
+
+ /**
+ * Returns the value of the given float sensor, if present in this frame.
+ * Returns null otherwise.
+ */
+ public @Nullable Float getSystemFloatSensor(
+ @android.car.diagnostic.FloatSensorIndex.SensorIndex int sensor) {
+ int index = floatValues.indexOfKey(sensor);
+ if (index < 0) return null;
+ return floatValues.valueAt(index);
+ }
+
+ /**
+ * Returns the value of the given integer sensor, if present in this frame.
+ * Returns null otherwise.
+ */
+ public @Nullable Integer getVendorIntegerSensor(int sensor) {
+ int index = intValues.indexOfKey(sensor);
+ if (index < 0) return null;
+ return intValues.valueAt(index);
+ }
+
+ /**
+ * Returns the value of the given float sensor, if present in this frame.
+ * Returns null otherwise.
+ */
+ public @Nullable Float getVendorFloatSensor(int sensor) {
+ int index = floatValues.indexOfKey(sensor);
+ if (index < 0) return null;
+ return floatValues.valueAt(index);
+ }
+
+ /**
+ * Represents possible states of the fuel system; see {@link
+ * android.car.diagnostic.IntegerSensorIndex#FUEL_SYSTEM_STATUS}
+ */
+ public static final class FuelSystemStatus {
+ private FuelSystemStatus() {}
+
+ public static final int OPEN_INSUFFICIENT_ENGINE_TEMPERATURE = 1;
+ public static final int CLOSED_LOOP = 2;
+ public static final int OPEN_ENGINE_LOAD_OR_DECELERATION = 4;
+ public static final int OPEN_SYSTEM_FAILURE = 8;
+ public static final int CLOSED_LOOP_BUT_FEEDBACK_FAULT = 16;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ OPEN_INSUFFICIENT_ENGINE_TEMPERATURE,
+ CLOSED_LOOP,
+ OPEN_ENGINE_LOAD_OR_DECELERATION,
+ OPEN_SYSTEM_FAILURE,
+ CLOSED_LOOP_BUT_FEEDBACK_FAULT
+ })
+ /** @hide */
+ public @interface Status {}
+ }
+
+ /**
+ * Represents possible states of the secondary air system; see {@link
+ * android.car.diagnostic.IntegerSensorIndex#COMMANDED_SECONDARY_AIR_STATUS}
+ */
+ public static final class SecondaryAirStatus {
+ private SecondaryAirStatus() {}
+
+ public static final int UPSTREAM = 1;
+ public static final int DOWNSTREAM_OF_CATALYCIC_CONVERTER = 2;
+ public static final int FROM_OUTSIDE_OR_OFF = 4;
+ public static final int PUMP_ON_FOR_DIAGNOSTICS = 8;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ UPSTREAM,
+ DOWNSTREAM_OF_CATALYCIC_CONVERTER,
+ FROM_OUTSIDE_OR_OFF,
+ PUMP_ON_FOR_DIAGNOSTICS
+ })
+ /** @hide */
+ public @interface Status {}
+ }
+
+ /**
+ * Represents possible types of fuel; see {@link
+ * android.car.diagnostic.IntegerSensorIndex#FUEL_TYPE}
+ */
+ public static final class FuelType {
+ private FuelType() {}
+
+ public static final int NOT_AVAILABLE = 0;
+ public static final int GASOLINE = 1;
+ public static final int METHANOL = 2;
+ public static final int ETHANOL = 3;
+ public static final int DIESEL = 4;
+ public static final int LPG = 5;
+ public static final int CNG = 6;
+ public static final int PROPANE = 7;
+ public static final int ELECTRIC = 8;
+ public static final int BIFUEL_RUNNING_GASOLINE = 9;
+ public static final int BIFUEL_RUNNING_METHANOL = 10;
+ public static final int BIFUEL_RUNNING_ETHANOL = 11;
+ public static final int BIFUEL_RUNNING_LPG = 12;
+ public static final int BIFUEL_RUNNING_CNG = 13;
+ public static final int BIFUEL_RUNNING_PROPANE = 14;
+ public static final int BIFUEL_RUNNING_ELECTRIC = 15;
+ public static final int BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION = 16;
+ public static final int HYBRID_GASOLINE = 17;
+ public static final int HYBRID_ETHANOL = 18;
+ public static final int HYBRID_DIESEL = 19;
+ public static final int HYBRID_ELECTRIC = 20;
+ public static final int HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION = 21;
+ public static final int HYBRID_REGENERATIVE = 22;
+ public static final int BIFUEL_RUNNING_DIESEL = 23;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ NOT_AVAILABLE,
+ GASOLINE,
+ METHANOL,
+ ETHANOL,
+ DIESEL,
+ LPG,
+ CNG,
+ PROPANE,
+ ELECTRIC,
+ BIFUEL_RUNNING_GASOLINE,
+ BIFUEL_RUNNING_METHANOL,
+ BIFUEL_RUNNING_ETHANOL,
+ BIFUEL_RUNNING_LPG,
+ BIFUEL_RUNNING_CNG,
+ BIFUEL_RUNNING_PROPANE,
+ BIFUEL_RUNNING_ELECTRIC,
+ BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION,
+ HYBRID_GASOLINE,
+ HYBRID_ETHANOL,
+ HYBRID_DIESEL,
+ HYBRID_ELECTRIC,
+ HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION,
+ HYBRID_REGENERATIVE,
+ BIFUEL_RUNNING_DIESEL
+ })
+ /** @hide */
+ public @interface Type {}
+ }
+
+ /**
+ * Represents the state of an ignition monitor on a vehicle.
+ */
+ public static final class IgnitionMonitor {
+ public final boolean available;
+ public final boolean incomplete;
+
+ IgnitionMonitor(boolean available, boolean incomplete) {
+ this.available = available;
+ this.incomplete = incomplete;
+ }
+
+ /** @hide */
+ public static final class Decoder {
+ private final int mAvailableBitmask;
+ private final int mIncompleteBitmask;
+
+ Decoder(int availableBitmask, int incompleteBitmask) {
+ mAvailableBitmask = availableBitmask;
+ mIncompleteBitmask = incompleteBitmask;
+ }
+
+ public IgnitionMonitor fromValue(int value) {
+ boolean available = (0 != (value & mAvailableBitmask));
+ boolean incomplete = (0 != (value & mIncompleteBitmask));
+
+ return new IgnitionMonitor(available, incomplete);
+ }
+ }
+ }
+
+ /**
+ * Contains information about ignition monitors common to all vehicle types.
+ */
+ public static class CommonIgnitionMonitors {
+ public final IgnitionMonitor components;
+ public final IgnitionMonitor fuelSystem;
+ public final IgnitionMonitor misfire;
+
+ /** @hide */
+ public static final int COMPONENTS_AVAILABLE = 0x1 << 0;
+ /** @hide */
+ public static final int COMPONENTS_INCOMPLETE = 0x1 << 1;
+
+ /** @hide */
+ public static final int FUEL_SYSTEM_AVAILABLE = 0x1 << 2;
+ /** @hide */
+ public static final int FUEL_SYSTEM_INCOMPLETE = 0x1 << 3;
+
+ /** @hide */
+ public static final int MISFIRE_AVAILABLE = 0x1 << 4;
+ /** @hide */
+ public static final int MISFIRE_INCOMPLETE = 0x1 << 5;
+
+ static final IgnitionMonitor.Decoder COMPONENTS_DECODER =
+ new IgnitionMonitor.Decoder(COMPONENTS_AVAILABLE, COMPONENTS_INCOMPLETE);
+
+ static final IgnitionMonitor.Decoder FUEL_SYSTEM_DECODER =
+ new IgnitionMonitor.Decoder(FUEL_SYSTEM_AVAILABLE, FUEL_SYSTEM_INCOMPLETE);
+
+ static final IgnitionMonitor.Decoder MISFIRE_DECODER =
+ new IgnitionMonitor.Decoder(MISFIRE_AVAILABLE, MISFIRE_INCOMPLETE);
+
+ CommonIgnitionMonitors(int bitmask) {
+ components = COMPONENTS_DECODER.fromValue(bitmask);
+ fuelSystem = FUEL_SYSTEM_DECODER.fromValue(bitmask);
+ misfire = MISFIRE_DECODER.fromValue(bitmask);
+ }
+
+ /**
+ * Returns data about ignition monitors specific to spark vehicles, if this
+ * object represents ignition monitors for a spark vehicle.
+ * Returns null otherwise.
+ */
+ public @Nullable SparkIgnitionMonitors asSparkIgnitionMonitors() {
+ if (this instanceof SparkIgnitionMonitors) return (SparkIgnitionMonitors) this;
+ return null;
+ }
+
+ /**
+ * Returns data about ignition monitors specific to compression vehicles, if this
+ * object represents ignition monitors for a compression vehicle.
+ * Returns null otherwise.
+ */
+ public @Nullable CompressionIgnitionMonitors asCompressionIgnitionMonitors() {
+ if (this instanceof CompressionIgnitionMonitors)
+ return (CompressionIgnitionMonitors) this;
+ return null;
+ }
+ }
+
+ /**
+ * Contains information about ignition monitors specific to spark vehicles.
+ */
+ public static final class SparkIgnitionMonitors extends CommonIgnitionMonitors {
+ public final IgnitionMonitor EGR;
+ public final IgnitionMonitor oxygenSensorHeater;
+ public final IgnitionMonitor oxygenSensor;
+ public final IgnitionMonitor ACRefrigerant;
+ public final IgnitionMonitor secondaryAirSystem;
+ public final IgnitionMonitor evaporativeSystem;
+ public final IgnitionMonitor heatedCatalyst;
+ public final IgnitionMonitor catalyst;
+
+ /** @hide */
+ public static final int EGR_AVAILABLE = 0x1 << 6;
+ /** @hide */
+ public static final int EGR_INCOMPLETE = 0x1 << 7;
+
+ /** @hide */
+ public static final int OXYGEN_SENSOR_HEATER_AVAILABLE = 0x1 << 8;
+ /** @hide */
+ public static final int OXYGEN_SENSOR_HEATER_INCOMPLETE = 0x1 << 9;
+
+ /** @hide */
+ public static final int OXYGEN_SENSOR_AVAILABLE = 0x1 << 10;
+ /** @hide */
+ public static final int OXYGEN_SENSOR_INCOMPLETE = 0x1 << 11;
+
+ /** @hide */
+ public static final int AC_REFRIGERANT_AVAILABLE = 0x1 << 12;
+ /** @hide */
+ public static final int AC_REFRIGERANT_INCOMPLETE = 0x1 << 13;
+
+ /** @hide */
+ public static final int SECONDARY_AIR_SYSTEM_AVAILABLE = 0x1 << 14;
+ /** @hide */
+ public static final int SECONDARY_AIR_SYSTEM_INCOMPLETE = 0x1 << 15;
+
+ /** @hide */
+ public static final int EVAPORATIVE_SYSTEM_AVAILABLE = 0x1 << 16;
+ /** @hide */
+ public static final int EVAPORATIVE_SYSTEM_INCOMPLETE = 0x1 << 17;
+
+ /** @hide */
+ public static final int HEATED_CATALYST_AVAILABLE = 0x1 << 18;
+ /** @hide */
+ public static final int HEATED_CATALYST_INCOMPLETE = 0x1 << 19;
+
+ /** @hide */
+ public static final int CATALYST_AVAILABLE = 0x1 << 20;
+ /** @hide */
+ public static final int CATALYST_INCOMPLETE = 0x1 << 21;
+
+ static final IgnitionMonitor.Decoder EGR_DECODER =
+ new IgnitionMonitor.Decoder(EGR_AVAILABLE, EGR_INCOMPLETE);
+
+ static final IgnitionMonitor.Decoder OXYGEN_SENSOR_HEATER_DECODER =
+ new IgnitionMonitor.Decoder(OXYGEN_SENSOR_HEATER_AVAILABLE,
+ OXYGEN_SENSOR_HEATER_INCOMPLETE);
+
+ static final IgnitionMonitor.Decoder OXYGEN_SENSOR_DECODER =
+ new IgnitionMonitor.Decoder(OXYGEN_SENSOR_AVAILABLE, OXYGEN_SENSOR_INCOMPLETE);
+
+ static final IgnitionMonitor.Decoder AC_REFRIGERANT_DECODER =
+ new IgnitionMonitor.Decoder(AC_REFRIGERANT_AVAILABLE,
+ AC_REFRIGERANT_INCOMPLETE);
+
+ static final IgnitionMonitor.Decoder SECONDARY_AIR_SYSTEM_DECODER =
+ new IgnitionMonitor.Decoder(SECONDARY_AIR_SYSTEM_AVAILABLE,
+ SECONDARY_AIR_SYSTEM_INCOMPLETE);
+
+ static final IgnitionMonitor.Decoder EVAPORATIVE_SYSTEM_DECODER =
+ new IgnitionMonitor.Decoder(EVAPORATIVE_SYSTEM_AVAILABLE,
+ EVAPORATIVE_SYSTEM_INCOMPLETE);
+
+ static final IgnitionMonitor.Decoder HEATED_CATALYST_DECODER =
+ new IgnitionMonitor.Decoder(HEATED_CATALYST_AVAILABLE,
+ HEATED_CATALYST_INCOMPLETE);
+
+ static final IgnitionMonitor.Decoder CATALYST_DECODER =
+ new IgnitionMonitor.Decoder(CATALYST_AVAILABLE, CATALYST_INCOMPLETE);
+
+ SparkIgnitionMonitors(int bitmask) {
+ super(bitmask);
+ EGR = EGR_DECODER.fromValue(bitmask);
+ oxygenSensorHeater = OXYGEN_SENSOR_HEATER_DECODER.fromValue(bitmask);
+ oxygenSensor = OXYGEN_SENSOR_DECODER.fromValue(bitmask);
+ ACRefrigerant = AC_REFRIGERANT_DECODER.fromValue(bitmask);
+ secondaryAirSystem = SECONDARY_AIR_SYSTEM_DECODER.fromValue(bitmask);
+ evaporativeSystem = EVAPORATIVE_SYSTEM_DECODER.fromValue(bitmask);
+ heatedCatalyst = HEATED_CATALYST_DECODER.fromValue(bitmask);
+ catalyst = CATALYST_DECODER.fromValue(bitmask);
+ }
+ }
+
+ /**
+ * Contains information about ignition monitors specific to compression vehicles.
+ */
+ public static final class CompressionIgnitionMonitors extends CommonIgnitionMonitors {
+ public final IgnitionMonitor EGROrVVT;
+ public final IgnitionMonitor PMFilter;
+ public final IgnitionMonitor exhaustGasSensor;
+ public final IgnitionMonitor boostPressure;
+ public final IgnitionMonitor NOxSCR;
+ public final IgnitionMonitor NMHCCatalyst;
+
+ /** @hide */
+ public static final int EGR_OR_VVT_AVAILABLE = 0x1 << 6;
+ /** @hide */
+ public static final int EGR_OR_VVT_INCOMPLETE = 0x1 << 7;
+
+ /** @hide */
+ public static final int PM_FILTER_AVAILABLE = 0x1 << 8;
+ /** @hide */
+ public static final int PM_FILTER_INCOMPLETE = 0x1 << 9;
+
+ /** @hide */
+ public static final int EXHAUST_GAS_SENSOR_AVAILABLE = 0x1 << 10;
+ /** @hide */
+ public static final int EXHAUST_GAS_SENSOR_INCOMPLETE = 0x1 << 11;
+
+ /** @hide */
+ public static final int BOOST_PRESSURE_AVAILABLE = 0x1 << 12;
+ /** @hide */
+ public static final int BOOST_PRESSURE_INCOMPLETE = 0x1 << 13;
+
+ /** @hide */
+ public static final int NOx_SCR_AVAILABLE = 0x1 << 14;
+ /** @hide */
+ public static final int NOx_SCR_INCOMPLETE = 0x1 << 15;
+
+ /** @hide */
+ public static final int NMHC_CATALYST_AVAILABLE = 0x1 << 16;
+ /** @hide */
+ public static final int NMHC_CATALYST_INCOMPLETE = 0x1 << 17;
+
+ static final IgnitionMonitor.Decoder EGR_OR_VVT_DECODER =
+ new IgnitionMonitor.Decoder(EGR_OR_VVT_AVAILABLE, EGR_OR_VVT_INCOMPLETE);
+
+ static final IgnitionMonitor.Decoder PM_FILTER_DECODER =
+ new IgnitionMonitor.Decoder(PM_FILTER_AVAILABLE, PM_FILTER_INCOMPLETE);
+
+ static final IgnitionMonitor.Decoder EXHAUST_GAS_SENSOR_DECODER =
+ new IgnitionMonitor.Decoder(EXHAUST_GAS_SENSOR_AVAILABLE,
+ EXHAUST_GAS_SENSOR_INCOMPLETE);
+
+ static final IgnitionMonitor.Decoder BOOST_PRESSURE_DECODER =
+ new IgnitionMonitor.Decoder(BOOST_PRESSURE_AVAILABLE,
+ BOOST_PRESSURE_INCOMPLETE);
+
+ static final IgnitionMonitor.Decoder NOx_SCR_DECODER =
+ new IgnitionMonitor.Decoder(NOx_SCR_AVAILABLE, NOx_SCR_INCOMPLETE);
+
+ static final IgnitionMonitor.Decoder NMHC_CATALYST_DECODER =
+ new IgnitionMonitor.Decoder(NMHC_CATALYST_AVAILABLE, NMHC_CATALYST_INCOMPLETE);
+
+ CompressionIgnitionMonitors(int bitmask) {
+ super(bitmask);
+ EGROrVVT = EGR_OR_VVT_DECODER.fromValue(bitmask);
+ PMFilter = PM_FILTER_DECODER.fromValue(bitmask);
+ exhaustGasSensor = EXHAUST_GAS_SENSOR_DECODER.fromValue(bitmask);
+ boostPressure = BOOST_PRESSURE_DECODER.fromValue(bitmask);
+ NOxSCR = NOx_SCR_DECODER.fromValue(bitmask);
+ NMHCCatalyst = NMHC_CATALYST_DECODER.fromValue(bitmask);
+ }
+ }
+
+ /**
+ * Returns the state of the fuel system, if present in this frame.
+ * Returns null otherwise.
+ */
+ public @Nullable @FuelSystemStatus.Status Integer getFuelSystemStatus() {
+ return getSystemIntegerSensor(android.car.diagnostic.IntegerSensorIndex.FUEL_SYSTEM_STATUS);
+ }
+
+ /**
+ * Returns the state of the secondary air system, if present in this frame.
+ * Returns null otherwise.
+ */
+ public @Nullable @SecondaryAirStatus.Status Integer getSecondaryAirStatus() {
+ return getSystemIntegerSensor(android.car.diagnostic.IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS);
+ }
+
+ /**
+ * Returns data about the ignition monitors, if present in this frame.
+ * Returns null otherwise.
+ */
+ public @Nullable CommonIgnitionMonitors getIgnitionMonitors() {
+ Integer ignitionMonitorsType =
+ getSystemIntegerSensor(android.car.diagnostic.IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED);
+ Integer ignitionMonitorsBitmask =
+ getSystemIntegerSensor(android.car.diagnostic.IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS);
+ if (null == ignitionMonitorsType) return null;
+ if (null == ignitionMonitorsBitmask) return null;
+ switch (ignitionMonitorsType) {
+ case 0:
+ return new SparkIgnitionMonitors(ignitionMonitorsBitmask);
+ case 1:
+ return new CompressionIgnitionMonitors(ignitionMonitorsBitmask);
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Returns the fuel type, if present in this frame.
+ * Returns null otherwise.
+ */
+ public @Nullable @FuelType.Type Integer getFuelType() {
+ return getSystemIntegerSensor(android.car.diagnostic.IntegerSensorIndex.FUEL_TYPE);
+ }
+}
diff --git a/car-lib/src/android/car/hardware/CarDiagnosticManager.java b/car-lib/src/android/car/diagnostic/CarDiagnosticManager.java
similarity index 81%
rename from car-lib/src/android/car/hardware/CarDiagnosticManager.java
rename to car-lib/src/android/car/diagnostic/CarDiagnosticManager.java
index 0444c14..84cdd3e 100644
--- a/car-lib/src/android/car/hardware/CarDiagnosticManager.java
+++ b/car-lib/src/android/car/diagnostic/CarDiagnosticManager.java
@@ -14,14 +14,17 @@
* limitations under the License.
*/
-package android.car.hardware;
+package android.car.diagnostic;
import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.car.Car;
import android.car.CarApiUtil;
import android.car.CarLibLog;
import android.car.CarManagerBase;
import android.car.CarNotConnectedException;
+import android.car.diagnostic.ICarDiagnosticEventListener.Stub;
import android.content.Context;
import android.os.Handler;
import android.os.IBinder;
@@ -40,16 +43,22 @@
import java.util.List;
import java.util.function.Consumer;
-/** API for monitoring car diagnostic data. */
-/** @hide */
+/**
+ * API for monitoring car diagnostic data.
+ *
+ * @hide
+ */
+@SystemApi
public final class CarDiagnosticManager implements CarManagerBase {
public static final int FRAME_TYPE_LIVE = 0;
public static final int FRAME_TYPE_FREEZE = 1;
@Retention(RetentionPolicy.SOURCE)
@IntDef({FRAME_TYPE_LIVE, FRAME_TYPE_FREEZE})
+ /** @hide */
public @interface FrameType {}
+ /** @hide */
public static final @FrameType int FRAME_TYPES[] = {
FRAME_TYPE_LIVE,
FRAME_TYPE_FREEZE
@@ -67,6 +76,7 @@
private final CarPermission mVendorExtensionPermission;
+ /** @hide */
public CarDiagnosticManager(IBinder service, Context context, Handler handler) {
mService = ICarDiagnostic.Stub.asInterface(service);
mHandlerCallback = new SingleMessageHandler<CarDiagnosticEvent>(handler.getLooper(),
@@ -86,6 +96,7 @@
}
@Override
+ /** @hide */
public void onCarDisconnected() {
synchronized(mActiveListeners) {
mActiveListeners.clear();
@@ -207,10 +218,12 @@
/**
* Retrieve the most-recently acquired live frame data from the car.
- * @return
+ * @return A CarDiagnostic event for the most recently known live frame if one is present.
+ * null if no live frame has been recorded by the vehicle.
* @throws CarNotConnectedException
*/
- public CarDiagnosticEvent getLatestLiveFrame() throws CarNotConnectedException {
+ public @Nullable
+ CarDiagnosticEvent getLatestLiveFrame() throws CarNotConnectedException {
try {
return mService.getLatestLiveFrame();
} catch (IllegalStateException e) {
@@ -223,7 +236,12 @@
/**
* Return the list of the timestamps for which a freeze frame is currently stored.
- * @return
+ * @return An array containing timestamps at which, at the current time, the vehicle has
+ * a freeze frame stored. If no freeze frames are currently stored, an empty
+ * array will be returned.
+ * Because vehicles might have a limited amount of storage for frames, clients cannot
+ * assume that a timestamp obtained via this call will be indefinitely valid for retrieval
+ * of the actual diagnostic data, and must be prepared to handle a missing frame.
* @throws CarNotConnectedException
*/
public long[] getFreezeFrameTimestamps() throws CarNotConnectedException {
@@ -240,10 +258,16 @@
/**
* Retrieve the freeze frame event data for a given timestamp, if available.
* @param timestamp
- * @return
+ * @return A CarDiagnostic event for the frame at the given timestamp, if one is
+ * available. null is returned otherwise.
+ * Storage constraints might cause frames to be deleted from vehicle memory.
+ * For this reason it cannot be assumed that a timestamp will yield a valid frame,
+ * even if it was initially obtained via a call to getFreezeFrameTimestamps().
* @throws CarNotConnectedException
*/
- public CarDiagnosticEvent getFreezeFrame(long timestamp) throws CarNotConnectedException {
+ public @Nullable
+ CarDiagnosticEvent getFreezeFrame(long timestamp)
+ throws CarNotConnectedException {
try {
return mService.getFreezeFrame(timestamp);
} catch (IllegalStateException e) {
@@ -256,8 +280,13 @@
/**
* Clear the freeze frame information from vehicle memory at the given timestamps.
- * @param timestamps
- * @return
+ * @param timestamps A list of timestamps to delete freeze frames at, or an empty array
+ * to delete all freeze frames from vehicle memory.
+ * @return true if all the required frames were deleted (including if no timestamps are
+ * provided and all frames were cleared); false otherwise.
+ * Due to storage constraints, timestamps cannot be assumed to be indefinitely valid, and
+ * a false return from this method should be used by the client as cause for invalidating
+ * its local knowledge of the vehicle diagnostic state.
* @throws CarNotConnectedException
*/
public boolean clearFreezeFrames(long... timestamps) throws CarNotConnectedException {
@@ -288,13 +317,13 @@
}
/**
- * Returns true if this vehicle supports sending freeze frame information.
- * @return
+ * Returns true if this vehicle supports supports sending notifications to
+ * registered listeners when new freeze frames happen.
* @throws CarNotConnectedException
*/
- public boolean isFreezeFrameSupported() throws CarNotConnectedException {
+ public boolean isFreezeFrameNotificationSupported() throws CarNotConnectedException {
try {
- return mService.isFreezeFrameSupported();
+ return mService.isFreezeFrameNotificationSupported();
} catch (IllegalStateException e) {
CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
} catch (RemoteException e) {
@@ -304,14 +333,13 @@
}
/**
- * Returns true if this vehicle supports retrieving freeze frame timestamps.
- * This is only meaningful if freeze frame data is also supported.
- * @return
+ * Returns whether the underlying HAL supports retrieving freeze frames
+ * stored in vehicle memory using timestamp.
* @throws CarNotConnectedException
*/
- public boolean isFreezeFrameTimestampSupported() throws CarNotConnectedException {
+ public boolean isGetFreezeFrameSupported() throws CarNotConnectedException {
try {
- return mService.isFreezeFrameTimestampSupported();
+ return mService.isGetFreezeFrameSupported();
} catch (IllegalStateException e) {
CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
} catch (RemoteException e) {
@@ -326,9 +354,9 @@
* @return
* @throws CarNotConnectedException
*/
- public boolean isFreezeFrameClearSupported() throws CarNotConnectedException {
+ public boolean isClearFreezeFramesSupported() throws CarNotConnectedException {
try {
- return mService.isFreezeFrameClearSupported();
+ return mService.isClearFreezeFramesSupported();
} catch (IllegalStateException e) {
CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
} catch (RemoteException e) {
@@ -338,7 +366,7 @@
}
private static class CarDiagnosticEventListenerToService
- extends ICarDiagnosticEventListener.Stub {
+ extends Stub {
private final WeakReference<CarDiagnosticManager> mManager;
public CarDiagnosticEventListenerToService(CarDiagnosticManager manager) {
diff --git a/car-lib/src/android/car/diagnostic/FloatSensorIndex.java b/car-lib/src/android/car/diagnostic/FloatSensorIndex.java
new file mode 100644
index 0000000..6ec7e8c
--- /dev/null
+++ b/car-lib/src/android/car/diagnostic/FloatSensorIndex.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package android.car.diagnostic;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class is a container for the indices of diagnostic sensors. The values are extracted by
+ * running packages/services/Car/tools/update-obd2-sensors.py against types.hal.
+ *
+ * DO NOT EDIT MANUALLY
+ *
+ * @hide
+ */
+@SystemApi
+public final class FloatSensorIndex {
+ private FloatSensorIndex() {}
+
+ public static final int CALCULATED_ENGINE_LOAD = 0;
+ public static final int ENGINE_COOLANT_TEMPERATURE = 1;
+ public static final int SHORT_TERM_FUEL_TRIM_BANK1 = 2;
+ public static final int LONG_TERM_FUEL_TRIM_BANK1 = 3;
+ public static final int SHORT_TERM_FUEL_TRIM_BANK2 = 4;
+ public static final int LONG_TERM_FUEL_TRIM_BANK2 = 5;
+ public static final int FUEL_PRESSURE = 6;
+ public static final int INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 7;
+ public static final int ENGINE_RPM = 8;
+ public static final int VEHICLE_SPEED = 9;
+ public static final int TIMING_ADVANCE = 10;
+ public static final int MAF_AIR_FLOW_RATE = 11;
+ public static final int THROTTLE_POSITION = 12;
+ public static final int OXYGEN_SENSOR1_VOLTAGE = 13;
+ public static final int OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM = 14;
+ public static final int OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO = 15;
+ public static final int OXYGEN_SENSOR2_VOLTAGE = 16;
+ public static final int OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM = 17;
+ public static final int OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO = 18;
+ public static final int OXYGEN_SENSOR3_VOLTAGE = 19;
+ public static final int OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM = 20;
+ public static final int OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO = 21;
+ public static final int OXYGEN_SENSOR4_VOLTAGE = 22;
+ public static final int OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM = 23;
+ public static final int OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO = 24;
+ public static final int OXYGEN_SENSOR5_VOLTAGE = 25;
+ public static final int OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM = 26;
+ public static final int OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO = 27;
+ public static final int OXYGEN_SENSOR6_VOLTAGE = 28;
+ public static final int OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM = 29;
+ public static final int OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO = 30;
+ public static final int OXYGEN_SENSOR7_VOLTAGE = 31;
+ public static final int OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM = 32;
+ public static final int OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO = 33;
+ public static final int OXYGEN_SENSOR8_VOLTAGE = 34;
+ public static final int OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM = 35;
+ public static final int OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO = 36;
+ public static final int FUEL_RAIL_PRESSURE = 37;
+ public static final int FUEL_RAIL_GAUGE_PRESSURE = 38;
+ public static final int COMMANDED_EXHAUST_GAS_RECIRCULATION = 39;
+ public static final int EXHAUST_GAS_RECIRCULATION_ERROR = 40;
+ public static final int COMMANDED_EVAPORATIVE_PURGE = 41;
+ public static final int FUEL_TANK_LEVEL_INPUT = 42;
+ public static final int EVAPORATION_SYSTEM_VAPOR_PRESSURE = 43;
+ public static final int CATALYST_TEMPERATURE_BANK1_SENSOR1 = 44;
+ public static final int CATALYST_TEMPERATURE_BANK2_SENSOR1 = 45;
+ public static final int CATALYST_TEMPERATURE_BANK1_SENSOR2 = 46;
+ public static final int CATALYST_TEMPERATURE_BANK2_SENSOR2 = 47;
+ public static final int ABSOLUTE_LOAD_VALUE = 48;
+ public static final int FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO = 49;
+ public static final int RELATIVE_THROTTLE_POSITION = 50;
+ public static final int ABSOLUTE_THROTTLE_POSITION_B = 51;
+ public static final int ABSOLUTE_THROTTLE_POSITION_C = 52;
+ public static final int ACCELERATOR_PEDAL_POSITION_D = 53;
+ public static final int ACCELERATOR_PEDAL_POSITION_E = 54;
+ public static final int ACCELERATOR_PEDAL_POSITION_F = 55;
+ public static final int COMMANDED_THROTTLE_ACTUATOR = 56;
+ public static final int ETHANOL_FUEL_PERCENTAGE = 57;
+ public static final int ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 58;
+ public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 59;
+ public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 60;
+ public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 61;
+ public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 62;
+ public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 63;
+ public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 64;
+ public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 65;
+ public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 66;
+ public static final int RELATIVE_ACCELERATOR_PEDAL_POSITION = 67;
+ public static final int HYBRID_BATTERY_PACK_REMAINING_LIFE = 68;
+ public static final int FUEL_INJECTION_TIMING = 69;
+ public static final int ENGINE_FUEL_RATE = 70;
+ public static final int LAST_SYSTEM = ENGINE_FUEL_RATE;
+ public static final int VENDOR_START = LAST_SYSTEM + 1;
+
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ FloatSensorIndex.CALCULATED_ENGINE_LOAD,
+ FloatSensorIndex.ENGINE_COOLANT_TEMPERATURE,
+ FloatSensorIndex.SHORT_TERM_FUEL_TRIM_BANK1,
+ FloatSensorIndex.LONG_TERM_FUEL_TRIM_BANK1,
+ FloatSensorIndex.SHORT_TERM_FUEL_TRIM_BANK2,
+ FloatSensorIndex.LONG_TERM_FUEL_TRIM_BANK2,
+ FloatSensorIndex.FUEL_PRESSURE,
+ FloatSensorIndex.INTAKE_MANIFOLD_ABSOLUTE_PRESSURE,
+ FloatSensorIndex.ENGINE_RPM,
+ FloatSensorIndex.VEHICLE_SPEED,
+ FloatSensorIndex.TIMING_ADVANCE,
+ FloatSensorIndex.MAF_AIR_FLOW_RATE,
+ FloatSensorIndex.THROTTLE_POSITION,
+ FloatSensorIndex.OXYGEN_SENSOR1_VOLTAGE,
+ FloatSensorIndex.OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM,
+ FloatSensorIndex.OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO,
+ FloatSensorIndex.OXYGEN_SENSOR2_VOLTAGE,
+ FloatSensorIndex.OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM,
+ FloatSensorIndex.OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO,
+ FloatSensorIndex.OXYGEN_SENSOR3_VOLTAGE,
+ FloatSensorIndex.OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM,
+ FloatSensorIndex.OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO,
+ FloatSensorIndex.OXYGEN_SENSOR4_VOLTAGE,
+ FloatSensorIndex.OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM,
+ FloatSensorIndex.OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO,
+ FloatSensorIndex.OXYGEN_SENSOR5_VOLTAGE,
+ FloatSensorIndex.OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM,
+ FloatSensorIndex.OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO,
+ FloatSensorIndex.OXYGEN_SENSOR6_VOLTAGE,
+ FloatSensorIndex.OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM,
+ FloatSensorIndex.OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO,
+ FloatSensorIndex.OXYGEN_SENSOR7_VOLTAGE,
+ FloatSensorIndex.OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM,
+ FloatSensorIndex.OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO,
+ FloatSensorIndex.OXYGEN_SENSOR8_VOLTAGE,
+ FloatSensorIndex.OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM,
+ FloatSensorIndex.OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO,
+ FloatSensorIndex.FUEL_RAIL_PRESSURE,
+ FloatSensorIndex.FUEL_RAIL_GAUGE_PRESSURE,
+ FloatSensorIndex.COMMANDED_EXHAUST_GAS_RECIRCULATION,
+ FloatSensorIndex.EXHAUST_GAS_RECIRCULATION_ERROR,
+ FloatSensorIndex.COMMANDED_EVAPORATIVE_PURGE,
+ FloatSensorIndex.FUEL_TANK_LEVEL_INPUT,
+ FloatSensorIndex.EVAPORATION_SYSTEM_VAPOR_PRESSURE,
+ FloatSensorIndex.CATALYST_TEMPERATURE_BANK1_SENSOR1,
+ FloatSensorIndex.CATALYST_TEMPERATURE_BANK2_SENSOR1,
+ FloatSensorIndex.CATALYST_TEMPERATURE_BANK1_SENSOR2,
+ FloatSensorIndex.CATALYST_TEMPERATURE_BANK2_SENSOR2,
+ FloatSensorIndex.ABSOLUTE_LOAD_VALUE,
+ FloatSensorIndex.FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO,
+ FloatSensorIndex.RELATIVE_THROTTLE_POSITION,
+ FloatSensorIndex.ABSOLUTE_THROTTLE_POSITION_B,
+ FloatSensorIndex.ABSOLUTE_THROTTLE_POSITION_C,
+ FloatSensorIndex.ACCELERATOR_PEDAL_POSITION_D,
+ FloatSensorIndex.ACCELERATOR_PEDAL_POSITION_E,
+ FloatSensorIndex.ACCELERATOR_PEDAL_POSITION_F,
+ FloatSensorIndex.COMMANDED_THROTTLE_ACTUATOR,
+ FloatSensorIndex.ETHANOL_FUEL_PERCENTAGE,
+ FloatSensorIndex.ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE,
+ FloatSensorIndex.SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1,
+ FloatSensorIndex.SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2,
+ FloatSensorIndex.SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3,
+ FloatSensorIndex.SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4,
+ FloatSensorIndex.LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1,
+ FloatSensorIndex.LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2,
+ FloatSensorIndex.LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3,
+ FloatSensorIndex.LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4,
+ FloatSensorIndex.RELATIVE_ACCELERATOR_PEDAL_POSITION,
+ FloatSensorIndex.HYBRID_BATTERY_PACK_REMAINING_LIFE,
+ FloatSensorIndex.FUEL_INJECTION_TIMING,
+ FloatSensorIndex.ENGINE_FUEL_RATE,
+ FloatSensorIndex.LAST_SYSTEM,
+ FloatSensorIndex.VENDOR_START,
+ })
+ public @interface SensorIndex {}
+
+}
diff --git a/car-lib/src/android/car/hardware/ICarDiagnostic.aidl b/car-lib/src/android/car/diagnostic/ICarDiagnostic.aidl
similarity index 79%
rename from car-lib/src/android/car/hardware/ICarDiagnostic.aidl
rename to car-lib/src/android/car/diagnostic/ICarDiagnostic.aidl
index 3afffc5..3d1808f 100644
--- a/car-lib/src/android/car/hardware/ICarDiagnostic.aidl
+++ b/car-lib/src/android/car/diagnostic/ICarDiagnostic.aidl
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package android.car.hardware;
+package android.car.diagnostic;
-import android.car.hardware.CarDiagnosticEvent;
-import android.car.hardware.ICarDiagnosticEventListener;
+import android.car.diagnostic.CarDiagnosticEvent;
+import android.car.diagnostic.ICarDiagnosticEventListener;
/** @hide */
interface ICarDiagnostic {
@@ -59,17 +59,19 @@
boolean isLiveFrameSupported() = 7;
/**
- * Returns whether the underlying HAL supports freeze frames.
+ * Returns whether the underlying HAL supports sending notifications to
+ * registered listeners when new freeze frames happen.
*/
- boolean isFreezeFrameSupported() = 8;
+ boolean isFreezeFrameNotificationSupported() = 8;
/**
- * Returns whether the underlying HAL supports retrieving freeze frame timestamps.
+ * Returns whether the underlying HAL supports retrieving freeze frames
+ * stored in vehicle memory using timestamp.
*/
- boolean isFreezeFrameTimestampSupported() = 9;
+ boolean isGetFreezeFrameSupported() = 9;
/**
* Returns whether the underlying HAL supports clearing freeze frames.
*/
- boolean isFreezeFrameClearSupported() = 10;
+ boolean isClearFreezeFramesSupported() = 10;
}
diff --git a/car-lib/src/android/car/hardware/ICarDiagnosticEventListener.aidl b/car-lib/src/android/car/diagnostic/ICarDiagnosticEventListener.aidl
similarity index 89%
rename from car-lib/src/android/car/hardware/ICarDiagnosticEventListener.aidl
rename to car-lib/src/android/car/diagnostic/ICarDiagnosticEventListener.aidl
index 3c9a189..89070aa 100644
--- a/car-lib/src/android/car/hardware/ICarDiagnosticEventListener.aidl
+++ b/car-lib/src/android/car/diagnostic/ICarDiagnosticEventListener.aidl
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package android.car.hardware;
+package android.car.diagnostic;
-import android.car.hardware.CarDiagnosticEvent;
+import android.car.diagnostic.CarDiagnosticEvent;
/**
* @hide
diff --git a/car-lib/src/android/car/diagnostic/IntegerSensorIndex.java b/car-lib/src/android/car/diagnostic/IntegerSensorIndex.java
new file mode 100644
index 0000000..15291f7
--- /dev/null
+++ b/car-lib/src/android/car/diagnostic/IntegerSensorIndex.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package android.car.diagnostic;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class is a container for the indices of diagnostic sensors. The values are extracted by
+ * running packages/services/Car/tools/update-obd2-sensors.py against types.hal.
+ *
+ * DO NOT EDIT MANUALLY
+ *
+ * @hide
+ */
+@SystemApi
+public final class IntegerSensorIndex {
+ private IntegerSensorIndex() {}
+
+ public static final int FUEL_SYSTEM_STATUS = 0;
+ public static final int MALFUNCTION_INDICATOR_LIGHT_ON = 1;
+ public static final int IGNITION_MONITORS_SUPPORTED = 2;
+ public static final int IGNITION_SPECIFIC_MONITORS = 3;
+ public static final int INTAKE_AIR_TEMPERATURE = 4;
+ public static final int COMMANDED_SECONDARY_AIR_STATUS = 5;
+ public static final int NUM_OXYGEN_SENSORS_PRESENT = 6;
+ public static final int RUNTIME_SINCE_ENGINE_START = 7;
+ public static final int DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON = 8;
+ public static final int WARMUPS_SINCE_CODES_CLEARED = 9;
+ public static final int DISTANCE_TRAVELED_SINCE_CODES_CLEARED = 10;
+ public static final int ABSOLUTE_BAROMETRIC_PRESSURE = 11;
+ public static final int CONTROL_MODULE_VOLTAGE = 12;
+ public static final int AMBIENT_AIR_TEMPERATURE = 13;
+ public static final int TIME_WITH_MALFUNCTION_LIGHT_ON = 14;
+ public static final int TIME_SINCE_TROUBLE_CODES_CLEARED = 15;
+ public static final int MAX_FUEL_AIR_EQUIVALENCE_RATIO = 16;
+ public static final int MAX_OXYGEN_SENSOR_VOLTAGE = 17;
+ public static final int MAX_OXYGEN_SENSOR_CURRENT = 18;
+ public static final int MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 19;
+ public static final int MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR = 20;
+ public static final int FUEL_TYPE = 21;
+ public static final int FUEL_RAIL_ABSOLUTE_PRESSURE = 22;
+ public static final int ENGINE_OIL_TEMPERATURE = 23;
+ public static final int DRIVER_DEMAND_PERCENT_TORQUE = 24;
+ public static final int ENGINE_ACTUAL_PERCENT_TORQUE = 25;
+ public static final int ENGINE_REFERENCE_PERCENT_TORQUE = 26;
+ public static final int ENGINE_PERCENT_TORQUE_DATA_IDLE = 27;
+ public static final int ENGINE_PERCENT_TORQUE_DATA_POINT1 = 28;
+ public static final int ENGINE_PERCENT_TORQUE_DATA_POINT2 = 29;
+ public static final int ENGINE_PERCENT_TORQUE_DATA_POINT3 = 30;
+ public static final int ENGINE_PERCENT_TORQUE_DATA_POINT4 = 31;
+ public static final int LAST_SYSTEM = ENGINE_PERCENT_TORQUE_DATA_POINT4;
+ public static final int VENDOR_START = LAST_SYSTEM + 1;
+
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ IntegerSensorIndex.FUEL_SYSTEM_STATUS,
+ IntegerSensorIndex.MALFUNCTION_INDICATOR_LIGHT_ON,
+ IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED,
+ IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS,
+ IntegerSensorIndex.INTAKE_AIR_TEMPERATURE,
+ IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS,
+ IntegerSensorIndex.NUM_OXYGEN_SENSORS_PRESENT,
+ IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START,
+ IntegerSensorIndex.DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON,
+ IntegerSensorIndex.WARMUPS_SINCE_CODES_CLEARED,
+ IntegerSensorIndex.DISTANCE_TRAVELED_SINCE_CODES_CLEARED,
+ IntegerSensorIndex.ABSOLUTE_BAROMETRIC_PRESSURE,
+ IntegerSensorIndex.CONTROL_MODULE_VOLTAGE,
+ IntegerSensorIndex.AMBIENT_AIR_TEMPERATURE,
+ IntegerSensorIndex.TIME_WITH_MALFUNCTION_LIGHT_ON,
+ IntegerSensorIndex.TIME_SINCE_TROUBLE_CODES_CLEARED,
+ IntegerSensorIndex.MAX_FUEL_AIR_EQUIVALENCE_RATIO,
+ IntegerSensorIndex.MAX_OXYGEN_SENSOR_VOLTAGE,
+ IntegerSensorIndex.MAX_OXYGEN_SENSOR_CURRENT,
+ IntegerSensorIndex.MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE,
+ IntegerSensorIndex.MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR,
+ IntegerSensorIndex.FUEL_TYPE,
+ IntegerSensorIndex.FUEL_RAIL_ABSOLUTE_PRESSURE,
+ IntegerSensorIndex.ENGINE_OIL_TEMPERATURE,
+ IntegerSensorIndex.DRIVER_DEMAND_PERCENT_TORQUE,
+ IntegerSensorIndex.ENGINE_ACTUAL_PERCENT_TORQUE,
+ IntegerSensorIndex.ENGINE_REFERENCE_PERCENT_TORQUE,
+ IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_IDLE,
+ IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_POINT1,
+ IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_POINT2,
+ IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_POINT3,
+ IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_POINT4,
+ IntegerSensorIndex.LAST_SYSTEM,
+ IntegerSensorIndex.VENDOR_START,
+ })
+ public @interface SensorIndex {}
+
+}
diff --git a/car-lib/src/android/car/hardware/CarDiagnosticEvent.java b/car-lib/src/android/car/hardware/CarDiagnosticEvent.java
deleted file mode 100644
index 1d82533..0000000
--- a/car-lib/src/android/car/hardware/CarDiagnosticEvent.java
+++ /dev/null
@@ -1,762 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.car.hardware;
-
-import android.annotation.IntDef;
-import android.annotation.Nullable;
-import android.car.annotation.FutureFeature;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.JsonWriter;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-
-/**
- * A CarDiagnosticEvent object corresponds to a single diagnostic event frame coming from the car.
- *
- * @hide
- */
-@FutureFeature
-public class CarDiagnosticEvent implements Parcelable {
- /** Whether this frame represents a live or a freeze frame */
- public final int frameType;
-
- /**
- * When this data was acquired in car or received from car. It is elapsed real-time of data
- * reception from car in nanoseconds since system boot.
- */
- public final long timestamp;
-
- /**
- * Sparse array that contains the mapping of OBD2 diagnostic properties to their values for
- * integer valued properties
- */
- private final SparseIntArray intValues;
-
- /**
- * Sparse array that contains the mapping of OBD2 diagnostic properties to their values for
- * float valued properties
- */
- private final SparseArray<Float> floatValues;
-
- /**
- * Diagnostic Troubleshooting Code (DTC) that was detected and caused this frame to be stored
- * (if a freeze frame). Always null for a live frame.
- */
- public final String dtc;
-
- public CarDiagnosticEvent(Parcel in) {
- frameType = in.readInt();
- timestamp = in.readLong();
- int len = in.readInt();
- floatValues = new SparseArray<>(len);
- for (int i = 0; i < len; ++i) {
- int key = in.readInt();
- float value = in.readFloat();
- floatValues.put(key, value);
- }
- len = in.readInt();
- intValues = new SparseIntArray(len);
- for (int i = 0; i < len; ++i) {
- int key = in.readInt();
- int value = in.readInt();
- intValues.put(key, value);
- }
- dtc = (String) in.readValue(String.class.getClassLoader());
- // version 1 up to here
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(frameType);
- dest.writeLong(timestamp);
- dest.writeInt(floatValues.size());
- for (int i = 0; i < floatValues.size(); ++i) {
- int key = floatValues.keyAt(i);
- dest.writeInt(key);
- dest.writeFloat(floatValues.get(key));
- }
- dest.writeInt(intValues.size());
- for (int i = 0; i < intValues.size(); ++i) {
- int key = intValues.keyAt(i);
- dest.writeInt(key);
- dest.writeInt(intValues.get(key));
- }
- dest.writeValue(dtc);
- }
-
- public void writeToJson(JsonWriter jsonWriter) throws IOException {
- jsonWriter.beginObject();
-
- jsonWriter.name("type");
- switch (frameType) {
- case CarDiagnosticManager.FRAME_TYPE_LIVE:
- jsonWriter.value("live");
- break;
- case CarDiagnosticManager.FRAME_TYPE_FREEZE:
- jsonWriter.value("freeze");
- break;
- default:
- throw new IllegalStateException("unknown frameType " + frameType);
- }
-
- jsonWriter.name("timestamp").value(timestamp);
-
- jsonWriter.name("intValues").beginArray();
- for (int i = 0; i < intValues.size(); ++i) {
- jsonWriter.beginObject();
- jsonWriter.name("id").value(intValues.keyAt(i));
- jsonWriter.name("value").value(intValues.valueAt(i));
- jsonWriter.endObject();
- }
- jsonWriter.endArray();
-
- jsonWriter.name("floatValues").beginArray();
- for (int i = 0; i < floatValues.size(); ++i) {
- jsonWriter.beginObject();
- jsonWriter.name("id").value(floatValues.keyAt(i));
- jsonWriter.name("value").value(floatValues.valueAt(i));
- jsonWriter.endObject();
- }
- jsonWriter.endArray();
-
- if (dtc != null) {
- jsonWriter.name("stringValue").value(dtc);
- }
-
- jsonWriter.endObject();
- }
-
- public static final Parcelable.Creator<CarDiagnosticEvent> CREATOR =
- new Parcelable.Creator<CarDiagnosticEvent>() {
- public CarDiagnosticEvent createFromParcel(Parcel in) {
- return new CarDiagnosticEvent(in);
- }
-
- public CarDiagnosticEvent[] newArray(int size) {
- return new CarDiagnosticEvent[size];
- }
- };
-
- private CarDiagnosticEvent(
- int frameType,
- long timestamp,
- SparseArray<Float> floatValues,
- SparseIntArray intValues,
- String dtc) {
- this.frameType = frameType;
- this.timestamp = timestamp;
- this.floatValues = floatValues;
- this.intValues = intValues;
- this.dtc = dtc;
- }
-
- public static class Builder {
- private int mType = CarDiagnosticManager.FRAME_TYPE_LIVE;
- private long mTimestamp = 0;
- private SparseArray<Float> mFloatValues = new SparseArray<>();
- private SparseIntArray mIntValues = new SparseIntArray();
- private String mDtc = null;
-
- private Builder(int type) {
- mType = type;
- }
-
- public static Builder newLiveFrameBuilder() {
- return new Builder(CarDiagnosticManager.FRAME_TYPE_LIVE);
- }
-
- public static Builder newFreezeFrameBuilder() {
- return new Builder(CarDiagnosticManager.FRAME_TYPE_FREEZE);
- }
-
- public Builder atTimestamp(long timestamp) {
- mTimestamp = timestamp;
- return this;
- }
-
- public Builder withIntValue(int key, int value) {
- mIntValues.put(key, value);
- return this;
- }
-
- public Builder withFloatValue(int key, float value) {
- mFloatValues.put(key, value);
- return this;
- }
-
- public Builder withDTC(String dtc) {
- mDtc = dtc;
- return this;
- }
-
- public CarDiagnosticEvent build() {
- return new CarDiagnosticEvent(mType, mTimestamp, mFloatValues, mIntValues, mDtc);
- }
- }
-
- /**
- * Returns a copy of this CarDiagnosticEvent with all vendor-specific sensors removed.
- *
- * @hide
- */
- public CarDiagnosticEvent withVendorSensorsRemoved() {
- SparseIntArray newIntValues = intValues.clone();
- SparseArray<Float> newFloatValues = floatValues.clone();
- for (int i = 0; i < intValues.size(); ++i) {
- int key = intValues.keyAt(i);
- if (key >= CarDiagnosticSensorIndices.Obd2IntegerSensorIndex.LAST_SYSTEM) {
- newIntValues.delete(key);
- }
- }
- for (int i = 0; i < floatValues.size(); ++i) {
- int key = floatValues.keyAt(i);
- if (key >= CarDiagnosticSensorIndices.Obd2FloatSensorIndex.LAST_SYSTEM) {
- newFloatValues.delete(key);
- }
- }
- return new CarDiagnosticEvent(frameType, timestamp, newFloatValues, newIntValues, dtc);
- }
-
- public boolean isLiveFrame() {
- return CarDiagnosticManager.FRAME_TYPE_LIVE == frameType;
- }
-
- public boolean isFreezeFrame() {
- return CarDiagnosticManager.FRAME_TYPE_FREEZE == frameType;
- }
-
- public boolean isEmptyFrame() {
- boolean empty = (0 == intValues.size());
- empty &= (0 == floatValues.size());
- if (isFreezeFrame()) empty &= dtc.isEmpty();
- return empty;
- }
-
- /** @hide */
- public CarDiagnosticEvent checkLiveFrame() {
- if (!isLiveFrame()) throw new IllegalStateException("frame is not a live frame");
- return this;
- }
-
- /** @hide */
- public CarDiagnosticEvent checkFreezeFrame() {
- if (!isFreezeFrame()) throw new IllegalStateException("frame is not a freeze frame");
- return this;
- }
-
- /** @hide */
- public boolean isEarlierThan(CarDiagnosticEvent otherEvent) {
- otherEvent = Objects.requireNonNull(otherEvent);
- return (timestamp < otherEvent.timestamp);
- }
-
- @Override
- public boolean equals(Object otherObject) {
- if (this == otherObject) {
- return true;
- }
- if (null == otherObject) {
- return false;
- }
- if (!(otherObject instanceof CarDiagnosticEvent)) {
- return false;
- }
- CarDiagnosticEvent otherEvent = (CarDiagnosticEvent)otherObject;
- if (otherEvent.frameType != frameType)
- return false;
- if (otherEvent.timestamp != timestamp)
- return false;
- if (otherEvent.intValues.size() != intValues.size())
- return false;
- if (otherEvent.floatValues.size() != floatValues.size())
- return false;
- if (!Objects.equals(dtc, otherEvent.dtc))
- return false;
- for (int i = 0; i < intValues.size(); ++i) {
- int key = intValues.keyAt(i);
- int otherKey = otherEvent.intValues.keyAt(i);
- if (key != otherKey) {
- return false;
- }
- int value = intValues.valueAt(i);
- int otherValue = otherEvent.intValues.valueAt(i);
- if (value != otherValue) {
- return false;
- }
- }
- for (int i = 0; i < floatValues.size(); ++i) {
- int key = floatValues.keyAt(i);
- int otherKey = otherEvent.floatValues.keyAt(i);
- if (key != otherKey) {
- return false;
- }
- float value = floatValues.valueAt(i);
- float otherValue = otherEvent.floatValues.valueAt(i);
- if (value != otherValue) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public int hashCode() {
- Integer[] intKeys = new Integer[intValues.size()];
- Integer[] floatKeys = new Integer[floatValues.size()];
- Integer[] intValues = new Integer[intKeys.length];
- Float[] floatValues = new Float[floatKeys.length];
- for (int i = 0; i < intKeys.length; ++i) {
- intKeys[i] = this.intValues.keyAt(i);
- intValues[i] = this.intValues.valueAt(i);
- }
- for (int i = 0; i < floatKeys.length; ++i) {
- floatKeys[i] = this.floatValues.keyAt(i);
- floatValues[i] = this.floatValues.valueAt(i);
- }
- int intKeysHash = Objects.hash((Object[])intKeys);
- int intValuesHash = Objects.hash((Object[])intValues);
- int floatKeysHash = Objects.hash((Object[])floatKeys);
- int floatValuesHash = Objects.hash((Object[])floatValues);
- return Objects.hash(frameType,
- timestamp,
- dtc,
- intKeysHash,
- intValuesHash,
- floatKeysHash,
- floatValuesHash);
- }
-
- @Override
- public String toString() {
- return String.format(
- "%s diagnostic frame {\n"
- + "\ttimestamp: %d, "
- + "DTC: %s\n"
- + "\tintValues: %s\n"
- + "\tfloatValues: %s\n}",
- isLiveFrame() ? "live" : "freeze",
- timestamp,
- dtc,
- intValues.toString(),
- floatValues.toString());
- }
-
- public int getSystemIntegerSensor(
- @CarDiagnosticSensorIndices.IntegerSensorIndex int sensor, int defaultValue) {
- return intValues.get(sensor, defaultValue);
- }
-
- public float getSystemFloatSensor(
- @CarDiagnosticSensorIndices.FloatSensorIndex int sensor, float defaultValue) {
- return floatValues.get(sensor, defaultValue);
- }
-
- public int getVendorIntegerSensor(int sensor, int defaultValue) {
- return intValues.get(sensor, defaultValue);
- }
-
- public float getVendorFloatSensor(int sensor, float defaultValue) {
- return floatValues.get(sensor, defaultValue);
- }
-
- public @Nullable Integer getSystemIntegerSensor(
- @CarDiagnosticSensorIndices.IntegerSensorIndex int sensor) {
- int index = intValues.indexOfKey(sensor);
- if (index < 0) return null;
- return intValues.valueAt(index);
- }
-
- public @Nullable Float getSystemFloatSensor(
- @CarDiagnosticSensorIndices.FloatSensorIndex int sensor) {
- int index = floatValues.indexOfKey(sensor);
- if (index < 0) return null;
- return floatValues.valueAt(index);
- }
-
- public @Nullable Integer getVendorIntegerSensor(int sensor) {
- int index = intValues.indexOfKey(sensor);
- if (index < 0) return null;
- return intValues.valueAt(index);
- }
-
- public @Nullable Float getVendorFloatSensor(int sensor) {
- int index = floatValues.indexOfKey(sensor);
- if (index < 0) return null;
- return floatValues.valueAt(index);
- }
-
- /**
- * Represents possible states of the fuel system; see {@link
- * CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#FUEL_SYSTEM_STATUS}
- */
- public static final class FuelSystemStatus {
- private FuelSystemStatus() {}
-
- public static final int OPEN_INSUFFICIENT_ENGINE_TEMPERATURE = 1;
- public static final int CLOSED_LOOP = 2;
- public static final int OPEN_ENGINE_LOAD_OR_DECELERATION = 4;
- public static final int OPEN_SYSTEM_FAILURE = 8;
- public static final int CLOSED_LOOP_BUT_FEEDBACK_FAULT = 16;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({
- OPEN_INSUFFICIENT_ENGINE_TEMPERATURE,
- CLOSED_LOOP,
- OPEN_ENGINE_LOAD_OR_DECELERATION,
- OPEN_SYSTEM_FAILURE,
- CLOSED_LOOP_BUT_FEEDBACK_FAULT
- })
- public @interface Status {}
- }
-
- /**
- * Represents possible states of the secondary air system; see {@link
- * CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#COMMANDED_SECONDARY_AIR_STATUS}
- */
- public static final class SecondaryAirStatus {
- private SecondaryAirStatus() {}
-
- public static final int UPSTREAM = 1;
- public static final int DOWNSTREAM_OF_CATALYCIC_CONVERTER = 2;
- public static final int FROM_OUTSIDE_OR_OFF = 4;
- public static final int PUMP_ON_FOR_DIAGNOSTICS = 8;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({
- UPSTREAM,
- DOWNSTREAM_OF_CATALYCIC_CONVERTER,
- FROM_OUTSIDE_OR_OFF,
- PUMP_ON_FOR_DIAGNOSTICS
- })
- public @interface Status {}
- }
-
- /**
- * Represents possible types of fuel; see {@link
- * CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#FUEL_TYPE}
- */
- public static final class FuelType {
- private FuelType() {}
-
- public static final int NOT_AVAILABLE = 0;
- public static final int GASOLINE = 1;
- public static final int METHANOL = 2;
- public static final int ETHANOL = 3;
- public static final int DIESEL = 4;
- public static final int LPG = 5;
- public static final int CNG = 6;
- public static final int PROPANE = 7;
- public static final int ELECTRIC = 8;
- public static final int BIFUEL_RUNNING_GASOLINE = 9;
- public static final int BIFUEL_RUNNING_METHANOL = 10;
- public static final int BIFUEL_RUNNING_ETHANOL = 11;
- public static final int BIFUEL_RUNNING_LPG = 12;
- public static final int BIFUEL_RUNNING_CNG = 13;
- public static final int BIFUEL_RUNNING_PROPANE = 14;
- public static final int BIFUEL_RUNNING_ELECTRIC = 15;
- public static final int BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION = 16;
- public static final int HYBRID_GASOLINE = 17;
- public static final int HYBRID_ETHANOL = 18;
- public static final int HYBRID_DIESEL = 19;
- public static final int HYBRID_ELECTRIC = 20;
- public static final int HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION = 21;
- public static final int HYBRID_REGENERATIVE = 22;
- public static final int BIFUEL_RUNNING_DIESEL = 23;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({
- NOT_AVAILABLE,
- GASOLINE,
- METHANOL,
- ETHANOL,
- DIESEL,
- LPG,
- CNG,
- PROPANE,
- ELECTRIC,
- BIFUEL_RUNNING_GASOLINE,
- BIFUEL_RUNNING_METHANOL,
- BIFUEL_RUNNING_ETHANOL,
- BIFUEL_RUNNING_LPG,
- BIFUEL_RUNNING_CNG,
- BIFUEL_RUNNING_PROPANE,
- BIFUEL_RUNNING_ELECTRIC,
- BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION,
- HYBRID_GASOLINE,
- HYBRID_ETHANOL,
- HYBRID_DIESEL,
- HYBRID_ELECTRIC,
- HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION,
- HYBRID_REGENERATIVE,
- BIFUEL_RUNNING_DIESEL
- })
- public @interface Type {}
- }
-
- /**
- * Represents possible states of the ignition monitors on the vehicle; see {@link
- * CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#IGNITION_MONITORS_SUPPORTED} see {@link
- * CarDiagnosticSensorIndices.Obd2IntegerSensorIndex#IGNITION_SPECIFIC_MONITORS}
- */
- public static final class IgnitionMonitors {
- public static final class IgnitionMonitor {
- public final boolean available;
- public final boolean incomplete;
-
- IgnitionMonitor(boolean available, boolean incomplete) {
- this.available = available;
- this.incomplete = incomplete;
- }
-
- public static final class Decoder {
- private final int mAvailableBitmask;
- private final int mIncompleteBitmask;
-
- Decoder(int availableBitmask, int incompleteBitmask) {
- mAvailableBitmask = availableBitmask;
- mIncompleteBitmask = incompleteBitmask;
- }
-
- public IgnitionMonitor fromValue(int value) {
- boolean available = (0 != (value & mAvailableBitmask));
- boolean incomplete = (0 != (value & mIncompleteBitmask));
-
- return new IgnitionMonitor(available, incomplete);
- }
- }
- }
-
- public static class CommonIgnitionMonitors {
- public final IgnitionMonitor components;
- public final IgnitionMonitor fuelSystem;
- public final IgnitionMonitor misfire;
-
- static final int COMPONENTS_AVAILABLE = 0x1 << 0;
- static final int COMPONENTS_INCOMPLETE = 0x1 << 1;
-
- static final int FUEL_SYSTEM_AVAILABLE = 0x1 << 2;
- static final int FUEL_SYSTEM_INCOMPLETE = 0x1 << 3;
-
- static final int MISFIRE_AVAILABLE = 0x1 << 4;
- static final int MISFIRE_INCOMPLETE = 0x1 << 5;
-
- static final IgnitionMonitor.Decoder COMPONENTS_DECODER =
- new IgnitionMonitor.Decoder(COMPONENTS_AVAILABLE, COMPONENTS_INCOMPLETE);
-
- static final IgnitionMonitor.Decoder FUEL_SYSTEM_DECODER =
- new IgnitionMonitor.Decoder(FUEL_SYSTEM_AVAILABLE, FUEL_SYSTEM_INCOMPLETE);
-
- static final IgnitionMonitor.Decoder MISFIRE_DECODER =
- new IgnitionMonitor.Decoder(MISFIRE_AVAILABLE, MISFIRE_INCOMPLETE);
-
- CommonIgnitionMonitors(int bitmask) {
- components = COMPONENTS_DECODER.fromValue(bitmask);
- fuelSystem = FUEL_SYSTEM_DECODER.fromValue(bitmask);
- misfire = MISFIRE_DECODER.fromValue(bitmask);
- }
-
- public @Nullable SparkIgnitionMonitors asSparkIgnitionMonitors() {
- if (this instanceof SparkIgnitionMonitors) return (SparkIgnitionMonitors) this;
- return null;
- }
-
- public @Nullable CompressionIgnitionMonitors asCompressionIgnitionMonitors() {
- if (this instanceof CompressionIgnitionMonitors)
- return (CompressionIgnitionMonitors) this;
- return null;
- }
- }
-
- public static final class SparkIgnitionMonitors extends CommonIgnitionMonitors {
- public final IgnitionMonitor EGR;
- public final IgnitionMonitor oxygenSensorHeater;
- public final IgnitionMonitor oxygenSensor;
- public final IgnitionMonitor ACRefrigerant;
- public final IgnitionMonitor secondaryAirSystem;
- public final IgnitionMonitor evaporativeSystem;
- public final IgnitionMonitor heatedCatalyst;
- public final IgnitionMonitor catalyst;
-
- static final int EGR_AVAILABLE = 0x1 << 6;
- static final int EGR_INCOMPLETE = 0x1 << 7;
-
- static final int OXYGEN_SENSOR_HEATER_AVAILABLE = 0x1 << 8;
- static final int OXYGEN_SENSOR_HEATER_INCOMPLETE = 0x1 << 9;
-
- static final int OXYGEN_SENSOR_AVAILABLE = 0x1 << 10;
- static final int OXYGEN_SENSOR_INCOMPLETE = 0x1 << 11;
-
- static final int AC_REFRIGERANT_AVAILABLE = 0x1 << 12;
- static final int AC_REFRIGERANT_INCOMPLETE = 0x1 << 13;
-
- static final int SECONDARY_AIR_SYSTEM_AVAILABLE = 0x1 << 14;
- static final int SECONDARY_AIR_SYSTEM_INCOMPLETE = 0x1 << 15;
-
- static final int EVAPORATIVE_SYSTEM_AVAILABLE = 0x1 << 16;
- static final int EVAPORATIVE_SYSTEM_INCOMPLETE = 0x1 << 17;
-
- static final int HEATED_CATALYST_AVAILABLE = 0x1 << 18;
- static final int HEATED_CATALYST_INCOMPLETE = 0x1 << 19;
-
- static final int CATALYST_AVAILABLE = 0x1 << 20;
- static final int CATALYST_INCOMPLETE = 0x1 << 21;
-
- static final IgnitionMonitor.Decoder EGR_DECODER =
- new IgnitionMonitor.Decoder(EGR_AVAILABLE, EGR_INCOMPLETE);
-
- static final IgnitionMonitor.Decoder OXYGEN_SENSOR_HEATER_DECODER =
- new IgnitionMonitor.Decoder(OXYGEN_SENSOR_HEATER_AVAILABLE,
- OXYGEN_SENSOR_HEATER_INCOMPLETE);
-
- static final IgnitionMonitor.Decoder OXYGEN_SENSOR_DECODER =
- new IgnitionMonitor.Decoder(OXYGEN_SENSOR_AVAILABLE, OXYGEN_SENSOR_INCOMPLETE);
-
- static final IgnitionMonitor.Decoder AC_REFRIGERANT_DECODER =
- new IgnitionMonitor.Decoder(AC_REFRIGERANT_AVAILABLE,
- AC_REFRIGERANT_INCOMPLETE);
-
- static final IgnitionMonitor.Decoder SECONDARY_AIR_SYSTEM_DECODER =
- new IgnitionMonitor.Decoder(SECONDARY_AIR_SYSTEM_AVAILABLE,
- SECONDARY_AIR_SYSTEM_INCOMPLETE);
-
- static final IgnitionMonitor.Decoder EVAPORATIVE_SYSTEM_DECODER =
- new IgnitionMonitor.Decoder(EVAPORATIVE_SYSTEM_AVAILABLE,
- EVAPORATIVE_SYSTEM_INCOMPLETE);
-
- static final IgnitionMonitor.Decoder HEATED_CATALYST_DECODER =
- new IgnitionMonitor.Decoder(HEATED_CATALYST_AVAILABLE,
- HEATED_CATALYST_INCOMPLETE);
-
- static final IgnitionMonitor.Decoder CATALYST_DECODER =
- new IgnitionMonitor.Decoder(CATALYST_AVAILABLE, CATALYST_INCOMPLETE);
-
- SparkIgnitionMonitors(int bitmask) {
- super(bitmask);
- EGR = EGR_DECODER.fromValue(bitmask);
- oxygenSensorHeater = OXYGEN_SENSOR_HEATER_DECODER.fromValue(bitmask);
- oxygenSensor = OXYGEN_SENSOR_DECODER.fromValue(bitmask);
- ACRefrigerant = AC_REFRIGERANT_DECODER.fromValue(bitmask);
- secondaryAirSystem = SECONDARY_AIR_SYSTEM_DECODER.fromValue(bitmask);
- evaporativeSystem = EVAPORATIVE_SYSTEM_DECODER.fromValue(bitmask);
- heatedCatalyst = HEATED_CATALYST_DECODER.fromValue(bitmask);
- catalyst = CATALYST_DECODER.fromValue(bitmask);
- }
- }
-
- public static final class CompressionIgnitionMonitors extends CommonIgnitionMonitors {
- public final IgnitionMonitor EGROrVVT;
- public final IgnitionMonitor PMFilter;
- public final IgnitionMonitor exhaustGasSensor;
- public final IgnitionMonitor boostPressure;
- public final IgnitionMonitor NOxSCR;
- public final IgnitionMonitor NMHCCatalyst;
-
- static final int EGR_OR_VVT_AVAILABLE = 0x1 << 6;
- static final int EGR_OR_VVT_INCOMPLETE = 0x1 << 7;
-
- static final int PM_FILTER_AVAILABLE = 0x1 << 8;
- static final int PM_FILTER_INCOMPLETE = 0x1 << 9;
-
- static final int EXHAUST_GAS_SENSOR_AVAILABLE = 0x1 << 10;
- static final int EXHAUST_GAS_SENSOR_INCOMPLETE = 0x1 << 11;
-
- static final int BOOST_PRESSURE_AVAILABLE = 0x1 << 12;
- static final int BOOST_PRESSURE_INCOMPLETE = 0x1 << 13;
-
- static final int NOx_SCR_AVAILABLE = 0x1 << 14;
- static final int NOx_SCR_INCOMPLETE = 0x1 << 15;
-
- static final int NMHC_CATALYST_AVAILABLE = 0x1 << 16;
- static final int NMHC_CATALYST_INCOMPLETE = 0x1 << 17;
-
- static final IgnitionMonitor.Decoder EGR_OR_VVT_DECODER =
- new IgnitionMonitor.Decoder(EGR_OR_VVT_AVAILABLE, EGR_OR_VVT_INCOMPLETE);
-
- static final IgnitionMonitor.Decoder PM_FILTER_DECODER =
- new IgnitionMonitor.Decoder(PM_FILTER_AVAILABLE, PM_FILTER_INCOMPLETE);
-
- static final IgnitionMonitor.Decoder EXHAUST_GAS_SENSOR_DECODER =
- new IgnitionMonitor.Decoder(EXHAUST_GAS_SENSOR_AVAILABLE,
- EXHAUST_GAS_SENSOR_INCOMPLETE);
-
- static final IgnitionMonitor.Decoder BOOST_PRESSURE_DECODER =
- new IgnitionMonitor.Decoder(BOOST_PRESSURE_AVAILABLE,
- BOOST_PRESSURE_INCOMPLETE);
-
- static final IgnitionMonitor.Decoder NOx_SCR_DECODER =
- new IgnitionMonitor.Decoder(NOx_SCR_AVAILABLE, NOx_SCR_INCOMPLETE);
-
- static final IgnitionMonitor.Decoder NMHC_CATALYST_DECODER =
- new IgnitionMonitor.Decoder(NMHC_CATALYST_AVAILABLE, NMHC_CATALYST_INCOMPLETE);
-
- CompressionIgnitionMonitors(int bitmask) {
- super(bitmask);
- EGROrVVT = EGR_OR_VVT_DECODER.fromValue(bitmask);
- PMFilter = PM_FILTER_DECODER.fromValue(bitmask);
- exhaustGasSensor = EXHAUST_GAS_SENSOR_DECODER.fromValue(bitmask);
- boostPressure = BOOST_PRESSURE_DECODER.fromValue(bitmask);
- NOxSCR = NOx_SCR_DECODER.fromValue(bitmask);
- NMHCCatalyst = NMHC_CATALYST_DECODER.fromValue(bitmask);
- }
- }
- }
-
- public @Nullable @FuelSystemStatus.Status Integer getFuelSystemStatus() {
- return getSystemIntegerSensor(
- CarDiagnosticSensorIndices.Obd2IntegerSensorIndex.FUEL_SYSTEM_STATUS);
- }
-
- public @Nullable @SecondaryAirStatus.Status Integer getSecondaryAirStatus() {
- return getSystemIntegerSensor(
- CarDiagnosticSensorIndices.Obd2IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS);
- }
-
- public @Nullable IgnitionMonitors.CommonIgnitionMonitors getIgnitionMonitors() {
- Integer ignitionMonitorsType =
- getSystemIntegerSensor(
- CarDiagnosticSensorIndices.Obd2IntegerSensorIndex
- .IGNITION_MONITORS_SUPPORTED);
- Integer ignitionMonitorsBitmask =
- getSystemIntegerSensor(
- CarDiagnosticSensorIndices.Obd2IntegerSensorIndex
- .IGNITION_SPECIFIC_MONITORS);
- if (null == ignitionMonitorsType) return null;
- if (null == ignitionMonitorsBitmask) return null;
- switch (ignitionMonitorsType) {
- case 0:
- return new IgnitionMonitors.SparkIgnitionMonitors(ignitionMonitorsBitmask);
- case 1:
- return new IgnitionMonitors.CompressionIgnitionMonitors(ignitionMonitorsBitmask);
- default:
- return null;
- }
- }
-
- public @Nullable @FuelType.Type Integer getFuelType() {
- return getSystemIntegerSensor(CarDiagnosticSensorIndices.Obd2IntegerSensorIndex.FUEL_TYPE);
- }
-}
diff --git a/car-lib/src/android/car/hardware/CarDiagnosticSensorIndices.java b/car-lib/src/android/car/hardware/CarDiagnosticSensorIndices.java
deleted file mode 100644
index d19b232..0000000
--- a/car-lib/src/android/car/hardware/CarDiagnosticSensorIndices.java
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
-*/
-
-package android.car.hardware;
-
-import android.annotation.IntDef;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * This class is a container for the indices of integer and float diagnostic sensors.
- * These values are extracted from types.hal by packages/services/Car/tools/update-obd2-sensors.py
- *
- * DO NOT EDIT MANUALLY
- *
- * @hide
- */
-public final class CarDiagnosticSensorIndices {
- public static final class Obd2IntegerSensorIndex {
- private Obd2IntegerSensorIndex() {}
-
- public static final int FUEL_SYSTEM_STATUS = 0;
- public static final int MALFUNCTION_INDICATOR_LIGHT_ON = 1;
- public static final int IGNITION_MONITORS_SUPPORTED = 2;
- public static final int IGNITION_SPECIFIC_MONITORS = 3;
- public static final int INTAKE_AIR_TEMPERATURE = 4;
- public static final int COMMANDED_SECONDARY_AIR_STATUS = 5;
- public static final int NUM_OXYGEN_SENSORS_PRESENT = 6;
- public static final int RUNTIME_SINCE_ENGINE_START = 7;
- public static final int DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON = 8;
- public static final int WARMUPS_SINCE_CODES_CLEARED = 9;
- public static final int DISTANCE_TRAVELED_SINCE_CODES_CLEARED = 10;
- public static final int ABSOLUTE_BAROMETRIC_PRESSURE = 11;
- public static final int CONTROL_MODULE_VOLTAGE = 12;
- public static final int AMBIENT_AIR_TEMPERATURE = 13;
- public static final int TIME_WITH_MALFUNCTION_LIGHT_ON = 14;
- public static final int TIME_SINCE_TROUBLE_CODES_CLEARED = 15;
- public static final int MAX_FUEL_AIR_EQUIVALENCE_RATIO = 16;
- public static final int MAX_OXYGEN_SENSOR_VOLTAGE = 17;
- public static final int MAX_OXYGEN_SENSOR_CURRENT = 18;
- public static final int MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 19;
- public static final int MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR = 20;
- public static final int FUEL_TYPE = 21;
- public static final int FUEL_RAIL_ABSOLUTE_PRESSURE = 22;
- public static final int ENGINE_OIL_TEMPERATURE = 23;
- public static final int DRIVER_DEMAND_PERCENT_TORQUE = 24;
- public static final int ENGINE_ACTUAL_PERCENT_TORQUE = 25;
- public static final int ENGINE_REFERENCE_PERCENT_TORQUE = 26;
- public static final int ENGINE_PERCENT_TORQUE_DATA_IDLE = 27;
- public static final int ENGINE_PERCENT_TORQUE_DATA_POINT1 = 28;
- public static final int ENGINE_PERCENT_TORQUE_DATA_POINT2 = 29;
- public static final int ENGINE_PERCENT_TORQUE_DATA_POINT3 = 30;
- public static final int ENGINE_PERCENT_TORQUE_DATA_POINT4 = 31;
- public static final int LAST_SYSTEM = ENGINE_PERCENT_TORQUE_DATA_POINT4;
- public static final int VENDOR_START = LAST_SYSTEM + 1;
- }
-
- public static final class Obd2FloatSensorIndex {
- private Obd2FloatSensorIndex() {}
-
- public static final int CALCULATED_ENGINE_LOAD = 0;
- public static final int ENGINE_COOLANT_TEMPERATURE = 1;
- public static final int SHORT_TERM_FUEL_TRIM_BANK1 = 2;
- public static final int LONG_TERM_FUEL_TRIM_BANK1 = 3;
- public static final int SHORT_TERM_FUEL_TRIM_BANK2 = 4;
- public static final int LONG_TERM_FUEL_TRIM_BANK2 = 5;
- public static final int FUEL_PRESSURE = 6;
- public static final int INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 7;
- public static final int ENGINE_RPM = 8;
- public static final int VEHICLE_SPEED = 9;
- public static final int TIMING_ADVANCE = 10;
- public static final int MAF_AIR_FLOW_RATE = 11;
- public static final int THROTTLE_POSITION = 12;
- public static final int OXYGEN_SENSOR1_VOLTAGE = 13;
- public static final int OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM = 14;
- public static final int OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO = 15;
- public static final int OXYGEN_SENSOR2_VOLTAGE = 16;
- public static final int OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM = 17;
- public static final int OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO = 18;
- public static final int OXYGEN_SENSOR3_VOLTAGE = 19;
- public static final int OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM = 20;
- public static final int OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO = 21;
- public static final int OXYGEN_SENSOR4_VOLTAGE = 22;
- public static final int OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM = 23;
- public static final int OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO = 24;
- public static final int OXYGEN_SENSOR5_VOLTAGE = 25;
- public static final int OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM = 26;
- public static final int OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO = 27;
- public static final int OXYGEN_SENSOR6_VOLTAGE = 28;
- public static final int OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM = 29;
- public static final int OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO = 30;
- public static final int OXYGEN_SENSOR7_VOLTAGE = 31;
- public static final int OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM = 32;
- public static final int OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO = 33;
- public static final int OXYGEN_SENSOR8_VOLTAGE = 34;
- public static final int OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM = 35;
- public static final int OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO = 36;
- public static final int FUEL_RAIL_PRESSURE = 37;
- public static final int FUEL_RAIL_GAUGE_PRESSURE = 38;
- public static final int COMMANDED_EXHAUST_GAS_RECIRCULATION = 39;
- public static final int EXHAUST_GAS_RECIRCULATION_ERROR = 40;
- public static final int COMMANDED_EVAPORATIVE_PURGE = 41;
- public static final int FUEL_TANK_LEVEL_INPUT = 42;
- public static final int EVAPORATION_SYSTEM_VAPOR_PRESSURE = 43;
- public static final int CATALYST_TEMPERATURE_BANK1_SENSOR1 = 44;
- public static final int CATALYST_TEMPERATURE_BANK2_SENSOR1 = 45;
- public static final int CATALYST_TEMPERATURE_BANK1_SENSOR2 = 46;
- public static final int CATALYST_TEMPERATURE_BANK2_SENSOR2 = 47;
- public static final int ABSOLUTE_LOAD_VALUE = 48;
- public static final int FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO = 49;
- public static final int RELATIVE_THROTTLE_POSITION = 50;
- public static final int ABSOLUTE_THROTTLE_POSITION_B = 51;
- public static final int ABSOLUTE_THROTTLE_POSITION_C = 52;
- public static final int ACCELERATOR_PEDAL_POSITION_D = 53;
- public static final int ACCELERATOR_PEDAL_POSITION_E = 54;
- public static final int ACCELERATOR_PEDAL_POSITION_F = 55;
- public static final int COMMANDED_THROTTLE_ACTUATOR = 56;
- public static final int ETHANOL_FUEL_PERCENTAGE = 57;
- public static final int ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 58;
- public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 59;
- public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 60;
- public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 61;
- public static final int SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 62;
- public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 63;
- public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 64;
- public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 65;
- public static final int LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 66;
- public static final int RELATIVE_ACCELERATOR_PEDAL_POSITION = 67;
- public static final int HYBRID_BATTERY_PACK_REMAINING_LIFE = 68;
- public static final int FUEL_INJECTION_TIMING = 69;
- public static final int ENGINE_FUEL_RATE = 70;
- public static final int LAST_SYSTEM = ENGINE_FUEL_RATE;
- public static final int VENDOR_START = LAST_SYSTEM + 1;
- }
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({
- Obd2IntegerSensorIndex.FUEL_SYSTEM_STATUS,
- Obd2IntegerSensorIndex.MALFUNCTION_INDICATOR_LIGHT_ON,
- Obd2IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED,
- Obd2IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS,
- Obd2IntegerSensorIndex.INTAKE_AIR_TEMPERATURE,
- Obd2IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS,
- Obd2IntegerSensorIndex.NUM_OXYGEN_SENSORS_PRESENT,
- Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START,
- Obd2IntegerSensorIndex.DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON,
- Obd2IntegerSensorIndex.WARMUPS_SINCE_CODES_CLEARED,
- Obd2IntegerSensorIndex.DISTANCE_TRAVELED_SINCE_CODES_CLEARED,
- Obd2IntegerSensorIndex.ABSOLUTE_BAROMETRIC_PRESSURE,
- Obd2IntegerSensorIndex.CONTROL_MODULE_VOLTAGE,
- Obd2IntegerSensorIndex.AMBIENT_AIR_TEMPERATURE,
- Obd2IntegerSensorIndex.TIME_WITH_MALFUNCTION_LIGHT_ON,
- Obd2IntegerSensorIndex.TIME_SINCE_TROUBLE_CODES_CLEARED,
- Obd2IntegerSensorIndex.MAX_FUEL_AIR_EQUIVALENCE_RATIO,
- Obd2IntegerSensorIndex.MAX_OXYGEN_SENSOR_VOLTAGE,
- Obd2IntegerSensorIndex.MAX_OXYGEN_SENSOR_CURRENT,
- Obd2IntegerSensorIndex.MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE,
- Obd2IntegerSensorIndex.MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR,
- Obd2IntegerSensorIndex.FUEL_TYPE,
- Obd2IntegerSensorIndex.FUEL_RAIL_ABSOLUTE_PRESSURE,
- Obd2IntegerSensorIndex.ENGINE_OIL_TEMPERATURE,
- Obd2IntegerSensorIndex.DRIVER_DEMAND_PERCENT_TORQUE,
- Obd2IntegerSensorIndex.ENGINE_ACTUAL_PERCENT_TORQUE,
- Obd2IntegerSensorIndex.ENGINE_REFERENCE_PERCENT_TORQUE,
- Obd2IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_IDLE,
- Obd2IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_POINT1,
- Obd2IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_POINT2,
- Obd2IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_POINT3,
- Obd2IntegerSensorIndex.ENGINE_PERCENT_TORQUE_DATA_POINT4,
- Obd2IntegerSensorIndex.LAST_SYSTEM,
- Obd2IntegerSensorIndex.VENDOR_START,
- })
- public @interface IntegerSensorIndex {}
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({
- Obd2FloatSensorIndex.CALCULATED_ENGINE_LOAD,
- Obd2FloatSensorIndex.ENGINE_COOLANT_TEMPERATURE,
- Obd2FloatSensorIndex.SHORT_TERM_FUEL_TRIM_BANK1,
- Obd2FloatSensorIndex.LONG_TERM_FUEL_TRIM_BANK1,
- Obd2FloatSensorIndex.SHORT_TERM_FUEL_TRIM_BANK2,
- Obd2FloatSensorIndex.LONG_TERM_FUEL_TRIM_BANK2,
- Obd2FloatSensorIndex.FUEL_PRESSURE,
- Obd2FloatSensorIndex.INTAKE_MANIFOLD_ABSOLUTE_PRESSURE,
- Obd2FloatSensorIndex.ENGINE_RPM,
- Obd2FloatSensorIndex.VEHICLE_SPEED,
- Obd2FloatSensorIndex.TIMING_ADVANCE,
- Obd2FloatSensorIndex.MAF_AIR_FLOW_RATE,
- Obd2FloatSensorIndex.THROTTLE_POSITION,
- Obd2FloatSensorIndex.OXYGEN_SENSOR1_VOLTAGE,
- Obd2FloatSensorIndex.OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM,
- Obd2FloatSensorIndex.OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO,
- Obd2FloatSensorIndex.OXYGEN_SENSOR2_VOLTAGE,
- Obd2FloatSensorIndex.OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM,
- Obd2FloatSensorIndex.OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO,
- Obd2FloatSensorIndex.OXYGEN_SENSOR3_VOLTAGE,
- Obd2FloatSensorIndex.OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM,
- Obd2FloatSensorIndex.OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO,
- Obd2FloatSensorIndex.OXYGEN_SENSOR4_VOLTAGE,
- Obd2FloatSensorIndex.OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM,
- Obd2FloatSensorIndex.OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO,
- Obd2FloatSensorIndex.OXYGEN_SENSOR5_VOLTAGE,
- Obd2FloatSensorIndex.OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM,
- Obd2FloatSensorIndex.OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO,
- Obd2FloatSensorIndex.OXYGEN_SENSOR6_VOLTAGE,
- Obd2FloatSensorIndex.OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM,
- Obd2FloatSensorIndex.OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO,
- Obd2FloatSensorIndex.OXYGEN_SENSOR7_VOLTAGE,
- Obd2FloatSensorIndex.OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM,
- Obd2FloatSensorIndex.OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO,
- Obd2FloatSensorIndex.OXYGEN_SENSOR8_VOLTAGE,
- Obd2FloatSensorIndex.OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM,
- Obd2FloatSensorIndex.OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO,
- Obd2FloatSensorIndex.FUEL_RAIL_PRESSURE,
- Obd2FloatSensorIndex.FUEL_RAIL_GAUGE_PRESSURE,
- Obd2FloatSensorIndex.COMMANDED_EXHAUST_GAS_RECIRCULATION,
- Obd2FloatSensorIndex.EXHAUST_GAS_RECIRCULATION_ERROR,
- Obd2FloatSensorIndex.COMMANDED_EVAPORATIVE_PURGE,
- Obd2FloatSensorIndex.FUEL_TANK_LEVEL_INPUT,
- Obd2FloatSensorIndex.EVAPORATION_SYSTEM_VAPOR_PRESSURE,
- Obd2FloatSensorIndex.CATALYST_TEMPERATURE_BANK1_SENSOR1,
- Obd2FloatSensorIndex.CATALYST_TEMPERATURE_BANK2_SENSOR1,
- Obd2FloatSensorIndex.CATALYST_TEMPERATURE_BANK1_SENSOR2,
- Obd2FloatSensorIndex.CATALYST_TEMPERATURE_BANK2_SENSOR2,
- Obd2FloatSensorIndex.ABSOLUTE_LOAD_VALUE,
- Obd2FloatSensorIndex.FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO,
- Obd2FloatSensorIndex.RELATIVE_THROTTLE_POSITION,
- Obd2FloatSensorIndex.ABSOLUTE_THROTTLE_POSITION_B,
- Obd2FloatSensorIndex.ABSOLUTE_THROTTLE_POSITION_C,
- Obd2FloatSensorIndex.ACCELERATOR_PEDAL_POSITION_D,
- Obd2FloatSensorIndex.ACCELERATOR_PEDAL_POSITION_E,
- Obd2FloatSensorIndex.ACCELERATOR_PEDAL_POSITION_F,
- Obd2FloatSensorIndex.COMMANDED_THROTTLE_ACTUATOR,
- Obd2FloatSensorIndex.ETHANOL_FUEL_PERCENTAGE,
- Obd2FloatSensorIndex.ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE,
- Obd2FloatSensorIndex.SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1,
- Obd2FloatSensorIndex.SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2,
- Obd2FloatSensorIndex.SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3,
- Obd2FloatSensorIndex.SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4,
- Obd2FloatSensorIndex.LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1,
- Obd2FloatSensorIndex.LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2,
- Obd2FloatSensorIndex.LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3,
- Obd2FloatSensorIndex.LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4,
- Obd2FloatSensorIndex.RELATIVE_ACCELERATOR_PEDAL_POSITION,
- Obd2FloatSensorIndex.HYBRID_BATTERY_PACK_REMAINING_LIFE,
- Obd2FloatSensorIndex.FUEL_INJECTION_TIMING,
- Obd2FloatSensorIndex.ENGINE_FUEL_RATE,
- Obd2FloatSensorIndex.LAST_SYSTEM,
- Obd2FloatSensorIndex.VENDOR_START,
- })
- public @interface FloatSensorIndex {}
-
-}
diff --git a/car-lib/src/android/car/hardware/CarDiagnosticEvent.aidl b/car-lib/src/android/car/hardware/CarSensorConfig.aidl
similarity index 95%
rename from car-lib/src/android/car/hardware/CarDiagnosticEvent.aidl
rename to car-lib/src/android/car/hardware/CarSensorConfig.aidl
index 73b184e..480f751 100644
--- a/car-lib/src/android/car/hardware/CarDiagnosticEvent.aidl
+++ b/car-lib/src/android/car/hardware/CarSensorConfig.aidl
@@ -16,4 +16,4 @@
package android.car.hardware;
-parcelable CarDiagnosticEvent;
+parcelable CarSensorConfig;
diff --git a/car-lib/src/android/car/hardware/CarSensorConfig.java b/car-lib/src/android/car/hardware/CarSensorConfig.java
new file mode 100644
index 0000000..8ba456b
--- /dev/null
+++ b/car-lib/src/android/car/hardware/CarSensorConfig.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.hardware;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import java.util.ArrayList;
+
+/**
+ * A CarSensorConfig object corresponds to a single sensor type coming from the car.
+ * @hide
+ */
+public class CarSensorConfig implements Parcelable {
+ /** List of property specific mapped elements in bundle for WHEEL_TICK_DISTANCE sensor*/
+ /** @hide */
+ public final static String WHEEL_TICK_DISTANCE_SUPPORTED_WHEELS =
+ "android.car.wheelTickDistanceSupportedWhheels";
+ /** @hide */
+ public final static String WHEEL_TICK_DISTANCE_FRONT_LEFT_UM_PER_TICK =
+ "android.car.wheelTickDistanceFrontLeftUmPerTick";
+ /** @hide */
+ public final static String WHEEL_TICK_DISTANCE_FRONT_RIGHT_UM_PER_TICK =
+ "android.car.wheelTickDistanceFrontRightUmPerTick";
+ /** @hide */
+ public final static String WHEEL_TICK_DISTANCE_REAR_RIGHT_UM_PER_TICK =
+ "android.car.wheelTickDistanceRearRightUmPerTick";
+ /** @hide */
+ public final static String WHEEL_TICK_DISTANCE_REAR_LEFT_UM_PER_TICK =
+ "android.car.wheelTickDistanceRearLeftUmPerTick";
+
+ /** Config data stored in Bundle */
+ private final Bundle mConfig;
+ private final int mType;
+
+ /** @hide */
+ public CarSensorConfig(Parcel in) {
+ mType = in.readInt();
+ mConfig = in.readBundle();
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeBundle(mConfig);
+ }
+
+ /** @hide */
+ public static final Parcelable.Creator<CarSensorConfig> CREATOR
+ = new Parcelable.Creator<CarSensorConfig>() {
+ public CarSensorConfig createFromParcel(Parcel in) {
+ return new CarSensorConfig(in);
+ }
+
+ public CarSensorConfig[] newArray(int size) {
+ return new CarSensorConfig[size];
+ }
+ };
+
+ /** @hide */
+ public CarSensorConfig(int type, Bundle b) {
+ mType = type;
+ mConfig = b.deepCopy();
+ }
+
+ private void checkType(int type) {
+ if (mType == type) {
+ return;
+ }
+ throw new UnsupportedOperationException(String.format(
+ "Invalid sensor type: expected %d, got %d", type, mType));
+ }
+
+ /** @hide */
+ public Bundle getBundle() {
+ return mConfig;
+ }
+
+ /** @hide */
+ public int getInt(String key) {
+ if (mConfig.containsKey(key)) {
+ return mConfig.getInt(key);
+ } else {
+ throw new IllegalArgumentException("SensorType " + mType +
+ " does not contain key: " + key);
+ }
+ }
+
+ /** @hide */
+ public int getType() {
+ return mType;
+ }
+
+ /** @hide */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(getClass().getName() + "[");
+ sb.append("mType: " + mType);
+ sb.append("mConfig: " + mConfig.toString());
+ sb.append("]");
+ return sb.toString();
+ }
+}
diff --git a/car-lib/src/android/car/hardware/CarSensorEvent.java b/car-lib/src/android/car/hardware/CarSensorEvent.java
index 680b9ab..52f363d 100644
--- a/car-lib/src/android/car/hardware/CarSensorEvent.java
+++ b/car-lib/src/android/car/hardware/CarSensorEvent.java
@@ -137,6 +137,17 @@
* Pressure in kPa.
*/
public static final int INDEX_ENVIRONMENT_PRESSURE = 1;
+ /**
+ * Index for {@link CarSensorManager#SENSOR_TYPE_WHEEL_TICK_DISTANCE} in longValues. RESET_COUNT
+ * is incremented whenever the HAL detects that a sensor reset has occurred. It represents to
+ * the upper layer that the WHEEL_DISTANCE values will not be contiguous with other values
+ * reported with a different RESET_COUNT.
+ */
+ public static final int INDEX_WHEEL_DISTANCE_RESET_COUNT = 0;
+ public static final int INDEX_WHEEL_DISTANCE_FRONT_LEFT = 1;
+ public static final int INDEX_WHEEL_DISTANCE_FRONT_RIGHT = 2;
+ public static final int INDEX_WHEEL_DISTANCE_REAR_RIGHT = 3;
+ public static final int INDEX_WHEEL_DISTANCE_REAR_LEFT = 4;
private static final long MILLI_IN_NANOS = 1000000L;
@@ -144,8 +155,8 @@
public int sensorType;
/**
- * When this data was acquired in car or received from car. It is elapsed real-time of data
- * reception from car in nanoseconds since system boot.
+ * When this data was received from car. It is elapsed real-time of data reception from car in
+ * nanoseconds since system boot.
*/
public long timestamp;
/**
@@ -154,6 +165,8 @@
public final float[] floatValues;
/** array holding int type of sensor data */
public final int[] intValues;
+ /** array holding long int type of sensor data */
+ public final long[] longValues;
/** @hide */
public CarSensorEvent(Parcel in) {
@@ -166,6 +179,9 @@
intValues = new int[len];
in.readIntArray(intValues);
// version 1 up to here
+ len = in.readInt();
+ longValues = new long[len];
+ in.readLongArray(longValues);
}
@Override
@@ -181,6 +197,8 @@
dest.writeFloatArray(floatValues);
dest.writeInt(intValues.length);
dest.writeIntArray(intValues);
+ dest.writeInt(longValues.length);
+ dest.writeLongArray(longValues);
}
public static final Parcelable.Creator<CarSensorEvent> CREATOR
@@ -195,19 +213,23 @@
};
/** @hide */
- public CarSensorEvent(int sensorType, long timestamp, int floatValueSize, int intValueSize) {
+ public CarSensorEvent(int sensorType, long timestamp, int floatValueSize, int intValueSize,
+ int longValueSize) {
this.sensorType = sensorType;
this.timestamp = timestamp;
floatValues = new float[floatValueSize];
intValues = new int[intValueSize];
+ longValues = new long[longValueSize];
}
/** @hide */
- CarSensorEvent(int sensorType, long timestamp, float[] floatValues, int[] intValues) {
+ CarSensorEvent(int sensorType, long timestamp, float[] floatValues, int[] intValues,
+ long[] longValues) {
this.sensorType = sensorType;
this.timestamp = timestamp;
this.floatValues = floatValues;
this.intValues = intValues;
+ this.longValues = longValues;
}
private void checkType(int type) {
@@ -493,6 +515,100 @@
}
/** @hide */
+ public static class CarWheelTickDistanceData {
+ public long timestamp;
+ public long sensorResetCount;
+ public long frontLeftWheelDistanceMm;
+ public long frontRightWheelDistanceMm;
+ public long rearRightWheelDistanceMm;
+ public long rearLeftWheelDistanceMm;
+
+ /** @hide */
+ private CarWheelTickDistanceData() {};
+ }
+
+ /**
+ * Convenience method for obtaining a {@link CarWheelTickDistanceData} object from a
+ * CarSensorEvent object with type {@link CarSensorManager#SENSOR_TYPE_WHEEL_TICK_DISTANCE}.
+ *
+ * @param data an optional output parameter which, if non-null, will be used by this method
+ * instead of a newly created object.
+ * @return CarWheelTickDistanceData object corresponding to data contained in the CarSensorEvent
+ * @hide
+ */
+ public CarWheelTickDistanceData getCarWheelTickDistanceData(CarWheelTickDistanceData data) {
+ checkType(CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE);
+ if (data == null) {
+ data = new CarWheelTickDistanceData();
+ }
+ data.timestamp = timestamp;
+ data.sensorResetCount = longValues[INDEX_WHEEL_DISTANCE_RESET_COUNT];
+ data.frontLeftWheelDistanceMm = longValues[INDEX_WHEEL_DISTANCE_FRONT_LEFT];
+ data.frontRightWheelDistanceMm = longValues[INDEX_WHEEL_DISTANCE_FRONT_RIGHT];
+ data.rearRightWheelDistanceMm = longValues[INDEX_WHEEL_DISTANCE_REAR_RIGHT];
+ data.rearLeftWheelDistanceMm = longValues[INDEX_WHEEL_DISTANCE_REAR_LEFT];
+ return data;
+ }
+
+ /** @hide */
+ public static class CarAbsActiveData {
+ public long timestamp;
+ public boolean absIsActive;
+
+ /** @hide */
+ private CarAbsActiveData() {};
+ }
+
+ /**
+ * Convenience method for obtaining a {@link CarAbsActiveData} object from a CarSensorEvent
+ * object with type {@link CarSensorManager#SENSOR_TYPE_ABS_ACTIVE}.
+ *
+ * @param data an optional output parameter which, if non-null, will be used by this method
+ * instead of a newly created object.
+ * @return a CarAbsActiveData object corresponding to data contained in the CarSensorEvent.
+ * @hide
+ */
+ public CarAbsActiveData getCarAbsActiveData(CarAbsActiveData data) {
+ checkType(CarSensorManager.SENSOR_TYPE_ABS_ACTIVE);
+ if (data == null) {
+ data = new CarAbsActiveData();
+ }
+ data.timestamp = timestamp;
+ data.absIsActive = intValues[0] == 1;
+ return data;
+ }
+
+ /** @hide */
+ public static class CarTractionControlActiveData {
+ public long timestamp;
+ public boolean tractionControlIsActive;
+
+ /** @hide */
+ private CarTractionControlActiveData() {};
+ }
+
+ /**
+ * Convenience method for obtaining a {@link CarTractionControlActiveData} object from a
+ * CarSensorEvent object with type {@link CarSensorManager#SENSOR_TYPE_TRACTION_CONTROL_ACTIVE}.
+ *
+ * @param data an optional output parameter which, if non-null, will be used by this method
+ * instead of a newly created object.
+ * @return a CarTractionControlActiveData object corresponding to data contained in the
+ * CarSensorEvent.
+ * @hide
+ */
+ public CarTractionControlActiveData getCarTractionControlActiveData(
+ CarTractionControlActiveData data) {
+ checkType(CarSensorManager.SENSOR_TYPE_TRACTION_CONTROL_ACTIVE);
+ if (data == null) {
+ data = new CarTractionControlActiveData();
+ }
+ data.timestamp = timestamp;
+ data.tractionControlIsActive = intValues[0] == 1;
+ return data;
+ }
+
+ /** @hide */
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -510,6 +626,12 @@
sb.append(" " + v);
}
}
+ if (longValues != null && longValues.length > 0) {
+ sb.append(" long values:");
+ for (long v: longValues) {
+ sb.append(" " + v);
+ }
+ }
sb.append("]");
return sb.toString();
}
diff --git a/car-lib/src/android/car/hardware/CarSensorManager.java b/car-lib/src/android/car/hardware/CarSensorManager.java
index 4fbc2cd..e2bce11 100644
--- a/car-lib/src/android/car/hardware/CarSensorManager.java
+++ b/car-lib/src/android/car/hardware/CarSensorManager.java
@@ -25,6 +25,7 @@
import android.car.CarManagerBase;
import android.car.CarNotConnectedException;
import android.content.Context;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -48,22 +49,22 @@
*/
public final class CarSensorManager implements CarManagerBase {
/** @hide */
- public static final int SENSOR_TYPE_RESERVED1 = 1;
+ public static final int SENSOR_TYPE_RESERVED1 = 1;
/**
* This sensor represents vehicle speed in m/s.
* Sensor data in {@link CarSensorEvent} is a float which will be >= 0.
* This requires {@link Car#PERMISSION_SPEED} permission.
*/
- public static final int SENSOR_TYPE_CAR_SPEED = 2;
+ public static final int SENSOR_TYPE_CAR_SPEED = 2;
/**
* Represents engine RPM of the car. Sensor data in {@link CarSensorEvent} is a float.
*/
- public static final int SENSOR_TYPE_RPM = 3;
+ public static final int SENSOR_TYPE_RPM = 3;
/**
* Total travel distance of the car in Kilometer. Sensor data is a float.
* This requires {@link Car#PERMISSION_MILEAGE} permission.
*/
- public static final int SENSOR_TYPE_ODOMETER = 4;
+ public static final int SENSOR_TYPE_ODOMETER = 4;
/**
* Indicates fuel level of the car.
* In {@link CarSensorEvent}, floatValues[{@link CarSensorEvent#INDEX_FUEL_LEVEL_IN_PERCENTILE}]
@@ -74,67 +75,84 @@
* condition.
* This requires {@link Car#PERMISSION_FUEL} permission.
*/
- public static final int SENSOR_TYPE_FUEL_LEVEL = 5;
+ public static final int SENSOR_TYPE_FUEL_LEVEL = 5;
/**
* Represents the current status of parking brake. Sensor data in {@link CarSensorEvent} is an
* intValues[0]. Value of 1 represents parking brake applied while 0 means the other way
* around. For this sensor, rate in {@link #registerListener(OnSensorChangedListener, int, int)}
* will be ignored and all changes will be notified.
*/
- public static final int SENSOR_TYPE_PARKING_BRAKE = 6;
+ public static final int SENSOR_TYPE_PARKING_BRAKE = 6;
/**
* This represents the current position of transmission gear. Sensor data in
* {@link CarSensorEvent} is an intValues[0]. For the meaning of the value, check
* {@link CarSensorEvent#GEAR_NEUTRAL} and other GEAR_*.
*/
- public static final int SENSOR_TYPE_GEAR = 7;
+ public static final int SENSOR_TYPE_GEAR = 7;
/** @hide */
- public static final int SENSOR_TYPE_RESERVED8 = 8;
+ public static final int SENSOR_TYPE_RESERVED8 = 8;
/**
* Day/night sensor. Sensor data is intValues[0].
*/
- public static final int SENSOR_TYPE_NIGHT = 9;
+ public static final int SENSOR_TYPE_NIGHT = 9;
/** @hide */
- public static final int SENSOR_TYPE_RESERVED10 = 10;
+ public static final int SENSOR_TYPE_RESERVED10 = 10;
/**
* Represents the current driving status of car. Different user interaction should be used
* depending on the current driving status. Driving status is intValues[0].
*/
- public static final int SENSOR_TYPE_DRIVING_STATUS = 11;
+ public static final int SENSOR_TYPE_DRIVING_STATUS = 11;
/**
* Environment like temperature and pressure.
*/
- public static final int SENSOR_TYPE_ENVIRONMENT = 12;
+ public static final int SENSOR_TYPE_ENVIRONMENT = 12;
/** @hide */
- public static final int SENSOR_TYPE_RESERVED13 = 13;
+ public static final int SENSOR_TYPE_RESERVED13 = 13;
/** @hide */
- public static final int SENSOR_TYPE_RESERVED14 = 14;
+ public static final int SENSOR_TYPE_RESERVED14 = 14;
/** @hide */
- public static final int SENSOR_TYPE_RESERVED15 = 15;
+ public static final int SENSOR_TYPE_RESERVED15 = 15;
/** @hide */
- public static final int SENSOR_TYPE_RESERVED16 = 16;
+ public static final int SENSOR_TYPE_RESERVED16 = 16;
/** @hide */
- public static final int SENSOR_TYPE_RESERVED17 = 17;
+ public static final int SENSOR_TYPE_RESERVED17 = 17;
/** @hide */
- public static final int SENSOR_TYPE_RESERVED18 = 18;
+ public static final int SENSOR_TYPE_RESERVED18 = 18;
/** @hide */
- public static final int SENSOR_TYPE_RESERVED19 = 19;
+ public static final int SENSOR_TYPE_RESERVED19 = 19;
/** @hide */
- public static final int SENSOR_TYPE_RESERVED20 = 20;
+ public static final int SENSOR_TYPE_RESERVED20 = 20;
/** @hide */
- public static final int SENSOR_TYPE_RESERVED21 = 21;
-
+ public static final int SENSOR_TYPE_RESERVED21 = 21;
/**
* Represents ignition state. The value should be one of the constants that starts with
* IGNITION_STATE_* in {@link CarSensorEvent}.
*/
- public static final int SENSOR_TYPE_IGNITION_STATE = 22;
+ public static final int SENSOR_TYPE_IGNITION_STATE = 22;
+ /**
+ * Represents wheel distance in millimeters. Some cars may not have individual sensors on each
+ * wheel. If a value is not available, Long.MAX_VALUE will be reported. The wheel distance
+ * accumulates over time. It increments on forward movement, and decrements on reverse. Wheel
+ * distance shall be reset to zero each time a vehicle is started by the user.
+ * This requires {@link Car#PERMISSION_SPEED} permission.
+ */
+ public static final int SENSOR_TYPE_WHEEL_TICK_DISTANCE = 23;
+ /**
+ * Set to true when ABS is active. This sensor is event driven.
+ * This requires {@link Car#PERMISSION_VEHICLE_DYNAMICS_STATE} permission.
+ */
+ public static final int SENSOR_TYPE_ABS_ACTIVE = 24;
+ /**
+ * Set to true when traction control is active. This sensor is event driven.
+ * This requires {@link Car#PERMISSION_VEHICLE_DYNAMICS_STATE} permission.
+ */
+ public static final int SENSOR_TYPE_TRACTION_CONTROL_ACTIVE = 25;
/**
* Sensor type bigger than this is invalid. Always update this after adding a new sensor.
* @hide
*/
- private static final int SENSOR_TYPE_MAX = SENSOR_TYPE_IGNITION_STATE;
+ private static final int SENSOR_TYPE_MAX = SENSOR_TYPE_TRACTION_CONTROL_ACTIVE;
/**
* Sensors defined in this range [{@link #SENSOR_TYPE_VENDOR_EXTENSION_START},
@@ -162,6 +180,9 @@
SENSOR_TYPE_DRIVING_STATUS,
SENSOR_TYPE_ENVIRONMENT,
SENSOR_TYPE_IGNITION_STATE,
+ SENSOR_TYPE_WHEEL_TICK_DISTANCE,
+ SENSOR_TYPE_ABS_ACTIVE,
+ SENSOR_TYPE_TRACTION_CONTROL_ACTIVE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SensorType {}
@@ -291,9 +312,11 @@
* If the same listener is registered again for the same sensor, it will be either ignored or
* updated depending on the rate.
* <p>
- * Requires {@link Car#PERMISSION_SPEED} for {@link #SENSOR_TYPE_CAR_SPEED},
- * {@link Car#PERMISSION_MILEAGE} for {@link #SENSOR_TYPE_ODOMETER},
- * or {@link Car#PERMISSION_FUEL} for {@link #SENSOR_TYPE_FUEL_LEVEL}.
+ * Requires {@link Car#PERMISSION_SPEED} for {@link #SENSOR_TYPE_CAR_SPEED} and
+ * {@link #SENSOR_TYPE_WHEEL_TICK_DISTANCE}, {@link Car#PERMISSION_MILEAGE} for
+ * {@link #SENSOR_TYPE_ODOMETER}, {@link Car#PERMISSION_FUEL} for
+ * {@link #SENSOR_TYPE_FUEL_LEVEL}, or {@link Car#PERMISSION_VEHICLE_DYNAMICS_STATE} for
+ * {@link #SENSOR_TYPE_ABS_ACTIVE} and {@link #SENSOR_TYPE_TRACTION_CONTROL_ACTIVE}
*
* @param listener
* @param sensorType sensor type to subscribe.
@@ -310,7 +333,8 @@
* @throws SecurityException if missing the appropriate permission
*/
@RequiresPermission(anyOf={Manifest.permission.ACCESS_FINE_LOCATION, Car.PERMISSION_SPEED,
- Car.PERMISSION_MILEAGE, Car.PERMISSION_FUEL}, conditional=true)
+ Car.PERMISSION_MILEAGE, Car.PERMISSION_FUEL, Car.PERMISSION_VEHICLE_DYNAMICS_STATE},
+ conditional=true)
public boolean registerListener(OnSensorChangedListener listener, @SensorType int sensorType,
@SensorRate int rate) throws CarNotConnectedException, IllegalArgumentException {
assertSensorType(sensorType);
@@ -490,4 +514,28 @@
});
}
}
+
+ /**
+ * Get the config data for the given type.
+ *
+ * A CarSensorConfig object is returned for every sensor type. However, if there is no
+ * config, the data will be empty.
+ *
+ * @param sensor type to request
+ * @return CarSensorConfig object
+ * @throws CarNotConnectedException if the connection to the car service has been lost.
+ * @hide
+ */
+ public CarSensorConfig getSensorConfig(@SensorType int type)
+ throws CarNotConnectedException {
+ assertSensorType(type);
+ try {
+ return mService.getSensorConfig(type);
+ } catch (IllegalStateException e) {
+ CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
+ } catch(RemoteException e) {
+ handleCarServiceRemoteExceptionAndThrow(e);
+ }
+ return new CarSensorConfig(0, Bundle.EMPTY);
+ }
}
diff --git a/car-lib/src/android/car/hardware/ICarSensor.aidl b/car-lib/src/android/car/hardware/ICarSensor.aidl
index 6f9e315..e943e0f 100644
--- a/car-lib/src/android/car/hardware/ICarSensor.aidl
+++ b/car-lib/src/android/car/hardware/ICarSensor.aidl
@@ -16,6 +16,7 @@
package android.car.hardware;
+import android.car.hardware.CarSensorConfig;
import android.car.hardware.CarSensorEvent;
import android.car.hardware.ICarSensorEventListener;
@@ -44,4 +45,9 @@
* be affected.
*/
void unregisterSensorListener(int sensorType, in ICarSensorEventListener callback) = 3;
+
+ /**
+ * get config flags and config array for the sensor type
+ */
+ CarSensorConfig getSensorConfig(int sensorType) = 4;
}
diff --git a/car-lib/src/android/car/hardware/hvac/CarHvacManager.java b/car-lib/src/android/car/hardware/hvac/CarHvacManager.java
index bebdd99..a2f6e6d 100644
--- a/car-lib/src/android/car/hardware/hvac/CarHvacManager.java
+++ b/car-lib/src/android/car/hardware/hvac/CarHvacManager.java
@@ -161,6 +161,11 @@
*/
public static final int ID_ZONED_MAX_DEFROST_ON = 0x400E;
/**
+ * Automatic recirculation mode ON
+ * true indicates recirculation is in automatic mode
+ */
+ public static final int ID_ZONED_HVAC_AUTO_RECIRC_ON = 0x400F;
+ /**
* Defroster ON, bool type
* Defroster controls are based on window position.
* True indicates the defroster is ON.
@@ -187,6 +192,7 @@
ID_ZONED_DUAL_ZONE_ON,
ID_ZONED_MAX_DEFROST_ON,
ID_ZONED_HVAC_POWER_ON,
+ ID_ZONED_HVAC_AUTO_RECIRC_ON,
ID_WINDOW_DEFROSTER_ON,
})
@Retention(RetentionPolicy.SOURCE)
diff --git a/car-lib/src/android/car/media/CarAudioManager.java b/car-lib/src/android/car/media/CarAudioManager.java
index cf7ce1f..49a7ca4 100644
--- a/car-lib/src/android/car/media/CarAudioManager.java
+++ b/car-lib/src/android/car/media/CarAudioManager.java
@@ -36,7 +36,7 @@
import java.lang.ref.WeakReference;
/**
- * APIs for handling car specific audio stuffs.
+ * APIs for handling car specific audio stuff.
*/
public final class CarAudioManager implements CarManagerBase {
@@ -81,10 +81,14 @@
*/
public static final int CAR_AUDIO_USAGE_SYSTEM_SAFETY_ALERT = 9;
/**
+ * Audio usage for the ringing of a phone call.
+ */
+ public static final int CAR_AUDIO_USAGE_RINGTONE = 10;
+ /**
* Audio usage for external audio usage.
* @hide
*/
- public static final int CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE = 10;
+ public static final int CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE = 11;
/** @hide */
public static final int CAR_AUDIO_USAGE_MAX = CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE;
diff --git a/car-lib/src/android/car/media/ICarAudio.aidl b/car-lib/src/android/car/media/ICarAudio.aidl
index f43a378..6d9b3ce 100644
--- a/car-lib/src/android/car/media/ICarAudio.aidl
+++ b/car-lib/src/android/car/media/ICarAudio.aidl
@@ -22,7 +22,7 @@
/**
* Binder interface for {@link android.car.media.CarAudioManager}.
- * Check {@link android.car.media.CarAudioManager} APIs for expected behavior of each calls.
+ * Check {@link android.car.media.CarAudioManager} APIs for expected behavior of each call.
*
* @hide
*/
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..e15a17d 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,22 @@
@Retention(RetentionPolicy.SOURCE)
public @interface TurnEvent {}
+ /**
+ * Event type that holds information about next maneuver.
+ * @hide
+ */
+ public static final int EVENT_TYPE_NEXT_MANEUVER_INFO = 1;
+ /**
+ * Event type that holds information regarding distance/time to the next maneuver.
+ * @hide
+ */
+ public static final int EVENT_TYPE_NEXT_MANEUVER_COUNTDOWN = 2;
+ /**
+ * All custom (vendor-specific) event types should be equal or greater than this constant.
+ * @hide
+ */
+ 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 +214,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 +228,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 +255,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 +271,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/car-lib/src/android/car/settings/CarSettings.java b/car-lib/src/android/car/settings/CarSettings.java
index 69e9a1e..6e2a8f4 100644
--- a/car-lib/src/android/car/settings/CarSettings.java
+++ b/car-lib/src/android/car/settings/CarSettings.java
@@ -74,6 +74,13 @@
*/
public static final String KEY_VOLUME_CALL = "android.car.VOLUME_CALL";
/**
+ * Key for phone ring volume. This is used internally, changing this value will not change
+ * the volume.
+ *
+ * @hide
+ */
+ public static final String KEY_VOLUME_RINGTONE = "android.car.VOLUME_RINGTONE";
+ /**
* Key for alarm volume. This is used internally, changing this value will not change the
* volume.
*
@@ -145,25 +152,81 @@
/**
* Key for a list of devices to automatically connect on Bluetooth A2dp/Avrcp profiles
- *
+ * Written to and read by {@link com.android.car.BluetoothDeviceConnectionPolicy}
* @hide
*/
public static final String KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICES =
"android.car.BLUETOOTH_AUTOCONNECT_MUSIC_DEVICES";
/**
* Key for a list of devices to automatically connect on Bluetooth HFP & PBAP profiles
+ * Written to and read by {@link com.android.car.BluetoothDeviceConnectionPolicy}
*
* @hide
*/
public static final String KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICES =
"android.car.BLUETOOTH_AUTOCONNECT_PHONE_DEVICES";
+
/**
* Key for a list of devices to automatically connect on Bluetooth MAP profile
- *
+ * Written to and read by {@link com.android.car.BluetoothDeviceConnectionPolicy}
* @hide
*/
public static final String KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICES =
"android.car.BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICES";
+ /**
+ * Key for setting primary Music Device
+ * Written to by a client with {@link com.android.car.Manifest.permission.BLUETOOTH_ADMIN}
+ * Read by {@link com.android.car.BluetoothDeviceConnectionPolicy}
+ * @hide
+ */
+ public static final String KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_0 =
+ "android.car.BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_0";
+
+ /**
+ * Key for setting secondary Music Device
+ * Written to by a client with {@link com.android.car.Manifest.permission.BLUETOOTH_ADMIN}
+ * Read by {@link com.android.car.BluetoothDeviceConnectionPolicy}
+ * @hide
+ */
+ public static final String KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_1 =
+ "android.car.BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_1";
+
+ /**
+ * Key for setting Primary Phone Device
+ * Written to by a client with {@link com.android.car.Manifest.permission.BLUETOOTH_ADMIN}
+ * Read by {@link com.android.car.BluetoothDeviceConnectionPolicy}
+ * @hide
+ */
+ public static final String KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_0 =
+ "android.car.BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_0";
+
+ /**
+ * Key for setting Secondary Phone Device
+ * Written to by a client with {@link com.android.car.Manifest.permission.BLUETOOTH_ADMIN}
+ * Read by {@link com.android.car.BluetoothDeviceConnectionPolicy}
+ * @hide
+ */
+ public static final String KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_1 =
+ "android.car.BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_1";
+
+ /**
+ * Key for setting Primary Messaging Device
+ * Written to by a client with {@link com.android.car.Manifest.permission.BLUETOOTH_ADMIN}
+ * Read by {@link com.android.car.BluetoothDeviceConnectionPolicy}
+ * @hide
+ */
+ public static final String KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_0 =
+ "android.car.BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_0";
+
+ /**
+ * Key for setting Secondary Messaging Device
+ * Written to by a client with {@link com.android.car.Manifest.permission.BLUETOOTH_ADMIN}
+ * Read by {@link com.android.car.BluetoothDeviceConnectionPolicy}
+ * @hide
+ */
+ public static final String KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_1 =
+ "android.car.BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_1";
+
}
}
diff --git a/car-lib/src/android/car/vms/IVmsPublisherService.aidl b/car-lib/src/android/car/vms/IVmsPublisherService.aidl
index 3312794..3c26a1b 100644
--- a/car-lib/src/android/car/vms/IVmsPublisherService.aidl
+++ b/car-lib/src/android/car/vms/IVmsPublisherService.aidl
@@ -29,7 +29,7 @@
/**
* Client call to publish a message.
*/
- oneway void publish(in IBinder token, in VmsLayer layer, in byte[] message) = 0;
+ oneway void publish(in IBinder token, in VmsLayer layer, int publisherId, in byte[] message) = 0;
/**
* Returns the list of VmsLayers that has any clients subscribed to it.
@@ -43,8 +43,8 @@
/**
* The first time a publisher calls this API it will store the publisher info and assigns the
- * publisher a static ID. Between reboots, subsequent calls with the same publisher info will
- * return the same ID so that a restarting process can obtain the same ID as it had before.
+ * publisher an ID. Between reboots, subsequent calls with the same publisher info will
+ * return the same ID so that a restarting process can obtain the same ID as it had before.
*/
- int getPublisherStaticId(in byte[] publisherInfo) = 3;
+ int getPublisherId(in byte[] publisherInfo) = 3;
}
diff --git a/car-lib/src/android/car/vms/IVmsSubscriberClient.aidl b/car-lib/src/android/car/vms/IVmsSubscriberClient.aidl
index 79c88ac..d5b5606 100644
--- a/car-lib/src/android/car/vms/IVmsSubscriberClient.aidl
+++ b/car-lib/src/android/car/vms/IVmsSubscriberClient.aidl
@@ -16,6 +16,7 @@
package android.car.vms;
+import android.car.vms.VmsAssociatedLayer;
import android.car.vms.VmsLayer;
/**
@@ -27,5 +28,5 @@
*/
void onVmsMessageReceived(in VmsLayer layer, in byte[] payload) = 0;
- void onLayersAvailabilityChange(in List<VmsLayer> availableLayers) = 1;
+ void onLayersAvailabilityChanged(in List<VmsAssociatedLayer> availableLayers) = 1;
}
diff --git a/car-lib/src/android/car/vms/IVmsSubscriberService.aidl b/car-lib/src/android/car/vms/IVmsSubscriberService.aidl
index 9234134..d10a6e4 100644
--- a/car-lib/src/android/car/vms/IVmsSubscriberService.aidl
+++ b/car-lib/src/android/car/vms/IVmsSubscriberService.aidl
@@ -24,40 +24,73 @@
*/
interface IVmsSubscriberService {
/**
- * Subscribes the listener to receive messages from layer/version.
+ * Adds a subscriber to notifications only.
+ * Should be called when a subscriber registers its callback, and before any subscription to a
+ * layer is made.
*/
- void addVmsSubscriberClientListener(
- in IVmsSubscriberClient listener,
- in VmsLayer layer) = 0;
+ void addVmsSubscriberToNotifications(
+ in IVmsSubscriberClient subscriber) = 0;
/**
- * Subscribes the listener to receive messages from all published layer/version. The
- * service will not send any subscription notifications to publishers (i.e. this is a passive
- * subscriber).
+ * Adds a subscriber to a VMS layer.
*/
- void addVmsSubscriberClientPassiveListener(in IVmsSubscriberClient listener) = 1;
+ void addVmsSubscriber(
+ in IVmsSubscriberClient subscriber,
+ in VmsLayer layer) = 1;
/**
- * Tells the VmsSubscriberService a client unsubscribes to layer messages.
- */
- void removeVmsSubscriberClientListener(
- in IVmsSubscriberClient listener,
- in VmsLayer layer) = 2;
+ * Adds a subscriber to all actively broadcasted layers.
+ * Publishers will not be notified regarding this request so the state of the service will not
+ * change.
+ */
+ void addVmsSubscriberPassive(in IVmsSubscriberClient subscriber) = 2;
/**
- * Tells the VmsSubscriberService a passive client unsubscribes. This will not unsubscribe
- * the listener from any specific layer it has subscribed to.
+ * Adds a subscriber to a VMS layer from a specific publisher.
+ */
+ void addVmsSubscriberToPublisher(
+ in IVmsSubscriberClient subscriber,
+ in VmsLayer layer,
+ int publisherId) = 3;
+
+ /**
+ * Removes a subscriber to notifications only.
+ * Should be called when a subscriber unregisters its callback, and after all subscriptions to
+ * layers are removed.
*/
- void removeVmsSubscriberClientPassiveListener(
- in IVmsSubscriberClient listener) = 3;
+ void removeVmsSubscriberToNotifications(
+ in IVmsSubscriberClient subscriber) = 4;
+
+ /**
+ * Removes a subscriber to a VMS layer.
+ */
+ void removeVmsSubscriber(
+ in IVmsSubscriberClient subscriber,
+ in VmsLayer layer) = 5;
+
+ /**
+ * Removes a subscriber to all actively broadcasted layers.
+ * Publishers will not be notified regarding this request so the state of the service will not
+ * change.
+ */
+ void removeVmsSubscriberPassive(
+ in IVmsSubscriberClient subscriber) = 6;
+
+ /**
+ * Removes a subscriber to a VMS layer from a specific publisher.
+ */
+ void removeVmsSubscriberToPublisher(
+ in IVmsSubscriberClient subscriber,
+ in VmsLayer layer,
+ int publisherId) = 7;
/**
* Returns a list of available layers from the closure of the publishers offerings.
*/
- List<VmsLayer> getAvailableLayers() = 4;
+ List<VmsLayer> getAvailableLayers() = 8;
/**
* Returns a the publisher information for a publisher ID.
*/
- byte[] getPublisherInfo(in int publisherId) = 5;
+ byte[] getPublisherInfo(in int publisherId) = 9;
}
diff --git a/car-lib/src/android/car/hardware/CarDiagnosticEvent.aidl b/car-lib/src/android/car/vms/VmsAssociatedLayer.aidl
similarity index 90%
copy from car-lib/src/android/car/hardware/CarDiagnosticEvent.aidl
copy to car-lib/src/android/car/vms/VmsAssociatedLayer.aidl
index 73b184e..c2cf271 100644
--- a/car-lib/src/android/car/hardware/CarDiagnosticEvent.aidl
+++ b/car-lib/src/android/car/vms/VmsAssociatedLayer.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.car.hardware;
+package android.car.vms;
-parcelable CarDiagnosticEvent;
+parcelable VmsAssociatedLayer;
\ No newline at end of file
diff --git a/car-lib/src/android/car/vms/VmsAssociatedLayer.java b/car-lib/src/android/car/vms/VmsAssociatedLayer.java
new file mode 100644
index 0000000..023fd9c
--- /dev/null
+++ b/car-lib/src/android/car/vms/VmsAssociatedLayer.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.vms;
+
+import android.car.annotation.FutureFeature;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.*;
+
+/**
+ * A VMS Layer with a list of publisher IDs it is associated with.
+ *
+ * @hide
+ */
+@FutureFeature
+public final class VmsAssociatedLayer implements Parcelable {
+
+ // The VmsLayer.
+ private final VmsLayer mLayer;
+
+ // The IDs of the publishers that can publish this VmsLayer.
+ private final Set<Integer> mPublisherIds;
+
+ public VmsAssociatedLayer(VmsLayer layer, Set<Integer> publisherIds) {
+ mLayer = layer;
+ mPublisherIds = Collections.unmodifiableSet(publisherIds);
+ }
+
+ public VmsLayer getVmsLayer() {
+ return mLayer;
+ }
+
+ public Set<Integer> getPublisherIds() {
+ return mPublisherIds;
+ }
+
+ @Override
+ public String toString() {
+ return "VmsAssociatedLayer{ VmsLayer: " + mLayer + ", Publishers: " + mPublisherIds + "}";
+ }
+
+ // Parcelable related methods.
+ public static final Parcelable.Creator<VmsAssociatedLayer> CREATOR =
+ new Parcelable.Creator<VmsAssociatedLayer>() {
+ public VmsAssociatedLayer createFromParcel(Parcel in) {
+ return new VmsAssociatedLayer(in);
+ }
+
+ public VmsAssociatedLayer[] newArray(int size) {
+ return new VmsAssociatedLayer[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(mLayer, flags);
+ out.writeArray(mPublisherIds.toArray());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof VmsAssociatedLayer)) {
+ return false;
+ }
+ VmsAssociatedLayer p = (VmsAssociatedLayer) o;
+ return Objects.equals(p.mLayer, mLayer) && p.mPublisherIds.equals(mPublisherIds);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mLayer, mPublisherIds);
+ }
+
+ private VmsAssociatedLayer(Parcel in) {
+ mLayer = in.readParcelable(VmsLayer.class.getClassLoader());
+
+ mPublisherIds = Collections.unmodifiableSet(
+ new HashSet<>(Arrays.asList(
+ (Integer[]) in.readArray(Integer.class.getClassLoader()))));
+ }
+}
\ No newline at end of file
diff --git a/car-lib/src/android/car/vms/VmsLayer.java b/car-lib/src/android/car/vms/VmsLayer.java
index afd0ae7..9125796 100644
--- a/car-lib/src/android/car/vms/VmsLayer.java
+++ b/car-lib/src/android/car/vms/VmsLayer.java
@@ -24,28 +24,33 @@
/**
* A VMS Layer which can be subscribed to by VMS clients.
- * Consists of the layer ID and the layer major version.
*
- * This class does not contain the minor version since all minor version are backward and forward
- * compatible and should not be used for routing messages.
* @hide
*/
@FutureFeature
public final class VmsLayer implements Parcelable {
- // The layer ID.
- private int mId;
+ // The layer Type.
+ private int mType;
+
+ // The layer Subtype.
+ private int mSubtype;
// The layer version.
private int mVersion;
- public VmsLayer(int id, int version) {
- mId = id;
+ public VmsLayer(int type, int subtype, int version) {
+ mType = type;
+ mSubtype = subtype;
mVersion = version;
}
- public int getId() {
- return mId;
+ public int getType() {
+ return mType;
+ }
+
+ public int getSubtype() {
+ return mSubtype;
}
public int getVersion() {
@@ -64,7 +69,9 @@
return false;
}
VmsLayer p = (VmsLayer) o;
- return Objects.equals(p.mId, mId) && Objects.equals(p.mVersion, mVersion);
+ return Objects.equals(p.mType, mType) &&
+ Objects.equals(p.mSubtype, mSubtype) &&
+ Objects.equals(p.mVersion, mVersion);
}
/**
@@ -74,12 +81,12 @@
*/
@Override
public int hashCode() {
- return Objects.hash(mId, mVersion);
+ return Objects.hash(mType, mSubtype, mVersion);
}
@Override
public String toString() {
- return "VmsLayer{" + mId + " " + mVersion + "}";
+ return "VmsLayer{ Type: " + mType + ", Sub type: " + mSubtype + ", Version: " + mVersion + "}";
}
@@ -97,7 +104,8 @@
@Override
public void writeToParcel(Parcel out, int flags) {
- out.writeInt(mId);
+ out.writeInt(mType);
+ out.writeInt(mSubtype);
out.writeInt(mVersion);
}
@@ -111,7 +119,8 @@
}
private void readFromParcel(Parcel in) {
- mId = in.readInt();
+ mType = in.readInt();
+ mSubtype = in.readInt();
mVersion = in.readInt();
}
}
\ No newline at end of file
diff --git a/car-lib/src/android/car/vms/VmsLayerDependency.java b/car-lib/src/android/car/vms/VmsLayerDependency.java
index e14c7ec..3b82775 100644
--- a/car-lib/src/android/car/vms/VmsLayerDependency.java
+++ b/car-lib/src/android/car/vms/VmsLayerDependency.java
@@ -52,13 +52,6 @@
mDependency = Collections.emptySet();
}
- /**
- * Checks if a layer has a dependency.
- */
- public boolean hasDependencies() {
- return (!mDependency.isEmpty());
- }
-
public VmsLayer getLayer() {
return mLayer;
}
diff --git a/car-lib/src/android/car/vms/VmsLayersOffering.java b/car-lib/src/android/car/vms/VmsLayersOffering.java
index 51a0b99..6abffc6 100644
--- a/car-lib/src/android/car/vms/VmsLayersOffering.java
+++ b/car-lib/src/android/car/vms/VmsLayersOffering.java
@@ -21,7 +21,9 @@
import android.os.Parcelable;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* The state of dependencies for a single publisher.
@@ -31,19 +33,26 @@
@FutureFeature
public final class VmsLayersOffering implements Parcelable {
- private final List<VmsLayerDependency> mDependencies;
+ private final Set<VmsLayerDependency> mDependencies;
- public VmsLayersOffering(List<VmsLayerDependency> dependencies) {
- mDependencies = Collections.unmodifiableList(dependencies);
+ private final int mPublisherId;
+
+ public VmsLayersOffering(Set<VmsLayerDependency> dependencies, int publisherId) {
+ mDependencies = Collections.unmodifiableSet(dependencies);
+ mPublisherId = publisherId;
}
/**
* Returns the dependencies.
*/
- public List<VmsLayerDependency> getDependencies() {
+ public Set<VmsLayerDependency> getDependencies() {
return mDependencies;
}
+ public int getPublisherId() {
+ return mPublisherId;
+ }
+
public static final Parcelable.Creator<VmsLayersOffering> CREATOR = new
Parcelable.Creator<VmsLayersOffering>() {
public VmsLayersOffering createFromParcel(Parcel in) {
@@ -61,7 +70,9 @@
@Override
public void writeToParcel(Parcel out, int flags) {
- out.writeParcelableList(mDependencies, flags);
+
+ out.writeParcelableList(new ArrayList(mDependencies), flags);
+ out.writeInt(mPublisherId);
}
@Override
@@ -72,6 +83,7 @@
private VmsLayersOffering(Parcel in) {
List<VmsLayerDependency> dependencies = new ArrayList<>();
in.readParcelableList(dependencies, VmsLayerDependency.class.getClassLoader());
- mDependencies = Collections.unmodifiableList(dependencies);
+ mDependencies = Collections.unmodifiableSet(new HashSet<>(dependencies));
+ mPublisherId = in.readInt();
}
}
\ No newline at end of file
diff --git a/car-lib/src/android/car/vms/VmsOperationRecorder.java b/car-lib/src/android/car/vms/VmsOperationRecorder.java
new file mode 100644
index 0000000..c873dce
--- /dev/null
+++ b/car-lib/src/android/car/vms/VmsOperationRecorder.java
@@ -0,0 +1,227 @@
+package android.car.vms;
+
+import android.car.annotation.FutureFeature;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Records VMS operations using the Android Log.
+ *
+ * This class records VMS operations. The recorded messages include the VMS operations and its
+ * arguments encoded as JSON text so that the string can be both read as a log message and easily
+ * parsed. VmsOperationRecorder is intended to be called after successful state change.
+ *
+ * Access the VmsOperationRecorder using the {@link #get()} method, which returns a singleton
+ * instance. Each VMS operation has a corresponding VmsOperationRecorder method. For instance:
+ * <pre>{@code
+ * VmsOperationRecorder.get().subscribe(layer);
+ * }</pre>
+ *
+ * @hide
+ */
+@FutureFeature
+public final class VmsOperationRecorder {
+ private static final String TAG = "VmsOperationRecorder";
+ private static final VmsOperationRecorder INSTANCE = new VmsOperationRecorder(new Writer());
+ private final Writer mWriter;
+
+ @VisibleForTesting
+ public VmsOperationRecorder(Writer writer) {
+ mWriter = writer;
+ }
+
+ /** Return the singleton instance. */
+ public static VmsOperationRecorder get() {
+ return INSTANCE;
+ }
+
+ // VMS Client operations.
+
+ public void subscribe(VmsLayer layer) {
+ recordOp("subscribe", layer);
+ }
+
+ public void unsubscribe(VmsLayer layer) {
+ recordOp("unsubscribe", layer);
+ }
+
+ public void subscribe(VmsLayer layer, int publisherId) {
+ recordOp("subscribe", "publisherId", publisherId, layer);
+ }
+
+ public void unsubscribe(VmsLayer layer, int publisherId) {
+ recordOp("unsubscribe", "publisherId", publisherId, layer);
+ }
+
+ public void startMonitoring() {
+ recordOp("startMonitoring");
+ }
+
+ public void stopMonitoring() {
+ recordOp("stopMonitoring");
+ }
+
+ public void setLayersOffering(VmsLayersOffering layersOffering) {
+ recordOp("setLayersOffering", layersOffering);
+ }
+
+ public void getPublisherId(int publisherId) {
+ recordOp("getPublisherId", "publisherId", publisherId);
+ }
+
+ // VMS Service operations.
+
+ public void addSubscription(int sequenceNumber, VmsLayer layer) {
+ recordOp("addSubscription", "sequenceNumber", sequenceNumber, layer);
+ }
+
+ public void removeSubscription(int sequenceNumber, VmsLayer layer) {
+ recordOp("removeSubscription", "sequenceNumber", sequenceNumber, layer);
+ }
+
+ public void addPromiscuousSubscription(int sequenceNumber) {
+ recordOp("addPromiscuousSubscription", "sequenceNumber", sequenceNumber);
+ }
+
+ public void removePromiscuousSubscription(int sequenceNumber) {
+ recordOp("removePromiscuousSubscription", "sequenceNumber", sequenceNumber);
+ }
+
+ public void addHalSubscription(int sequenceNumber, VmsLayer layer) {
+ recordOp("addHalSubscription", "sequenceNumber", sequenceNumber, layer);
+ }
+
+ public void removeHalSubscription(int sequenceNumber, VmsLayer layer) {
+ recordOp("removeHalSubscription", "sequenceNumber", sequenceNumber, layer);
+ }
+
+ public void setPublisherLayersOffering(VmsLayersOffering layersOffering) {
+ recordOp("setPublisherLayersOffering", layersOffering);
+ }
+
+ public void setHalPublisherLayersOffering(VmsLayersOffering layersOffering) {
+ recordOp("setHalPublisherLayersOffering", layersOffering);
+ }
+
+ private void recordOp(String operation) {
+ if (isEnabled()) {
+ try {
+ write(new JSONObject().put(operation, new JSONObject()));
+ } catch (JSONException e) {
+ Log.e(TAG, e.toString());
+ }
+ }
+ }
+
+ private void recordOp(String operation, VmsLayer layer) {
+ if (isEnabled()) {
+ try {
+ recordOp(operation, new JSONObject().put("layer", toJson(layer)));
+ } catch (JSONException e) {
+ Log.e(TAG, e.toString());
+ }
+ }
+ }
+
+ private void recordOp(String operation, VmsLayersOffering layersOffering) {
+ if (isEnabled()) {
+ try {
+ JSONObject args = new JSONObject();
+ JSONArray offering = toJson(layersOffering);
+ if (offering.length() > 0) {
+ args.put("layerDependency", offering);
+ }
+ recordOp(operation, args);
+ } catch (JSONException e) {
+ Log.e(TAG, e.toString());
+ }
+ }
+ }
+
+ private void recordOp(String operation, String intArgName, int arg) {
+ if (isEnabled()) {
+ try {
+ recordOp(operation, new JSONObject().put(intArgName, arg));
+ } catch (JSONException e) {
+ Log.e(TAG, e.toString());
+ }
+ }
+ }
+
+ private void recordOp(String operation, String intArgName, int arg, VmsLayer layer) {
+ if (isEnabled()) {
+ try {
+ recordOp(operation,
+ new JSONObject().put(intArgName, arg).put("layer", toJson(layer)));
+ } catch (JSONException e) {
+ Log.e(TAG, e.toString());
+ }
+ }
+ }
+
+ private void recordOp(String operation, JSONObject args) {
+ if (isEnabled()) {
+ try {
+ write(new JSONObject().put(operation, args));
+ } catch (JSONException e) {
+ Log.e(TAG, e.toString());
+ }
+ }
+ }
+
+ private static JSONObject toJson(VmsLayer layer) throws JSONException {
+ return new JSONObject()
+ .put("type", layer.getType())
+ .put("subtype", layer.getSubtype())
+ .put("version", layer.getVersion());
+ }
+
+ private static JSONObject toJson(VmsLayerDependency layerDependency) throws JSONException {
+ JSONObject dep = new JSONObject();
+ dep.put("layer", toJson(layerDependency.getLayer()));
+ if (!layerDependency.getDependencies().isEmpty()) {
+ JSONArray dependencies = new JSONArray();
+ for (VmsLayer dependency : layerDependency.getDependencies()) {
+ dependencies.put(toJson(dependency));
+ }
+ dep.put("dependency", dependencies);
+ }
+ return dep;
+ }
+
+ private static JSONArray toJson(VmsLayersOffering layersOffering) throws JSONException {
+ JSONArray offerings = new JSONArray();
+ for (VmsLayerDependency layerDependency : layersOffering.getDependencies()) {
+ offerings.put(toJson(layerDependency));
+ }
+ return offerings;
+ }
+
+ private boolean isEnabled() {
+ return mWriter.isEnabled();
+ }
+
+ private void write(JSONObject object) {
+ mWriter.write(object.toString());
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public static class Writer {
+ private static final String TAG = "VMS.RECORD.EVENT";
+ private static final int LEVEL = Log.DEBUG;
+
+ public boolean isEnabled() {
+ return Log.isLoggable(TAG, LEVEL);
+ }
+
+ public void write(String msg) {
+ Log.println(LEVEL, TAG, msg);
+ }
+ }
+}
diff --git a/car-lib/src/android/car/vms/VmsPublisherClientService.java b/car-lib/src/android/car/vms/VmsPublisherClientService.java
index ea265df..33ca1f3 100644
--- a/car-lib/src/android/car/vms/VmsPublisherClientService.java
+++ b/car-lib/src/android/car/vms/VmsPublisherClientService.java
@@ -31,7 +31,6 @@
import com.android.internal.annotations.GuardedBy;
import java.lang.ref.WeakReference;
-import java.util.List;
/**
* Services that need VMS publisher services need to inherit from this class and also need to be
@@ -40,8 +39,8 @@
* of the target product.
*
* The {@link com.android.car.VmsPublisherService} will start this service. The callback
- * {@link #onVmsPublisherServiceReady()} notifies when VMS publisher services (i.e.
- * {@link #publish(int, int, byte[])} and {@link #getSubscribers()}) can be used.
+ * {@link #onVmsPublisherServiceReady()} notifies when VMS publisher services can be used, and the
+ * publisher can request a publisher ID in order to start publishing.
*
* SystemApi candidate.
*
@@ -77,7 +76,7 @@
return super.onUnbind(intent);
}
- public void setToken(IBinder token) {
+ private void setToken(IBinder token) {
synchronized (mLock) {
mToken = token;
}
@@ -86,12 +85,12 @@
/**
* Notifies that the publisher services are ready.
*/
- public abstract void onVmsPublisherServiceReady();
+ protected abstract void onVmsPublisherServiceReady();
/**
* Publishers need to implement this method to receive notifications of subscription changes.
*
- * @param subscriptionState layers with subscribers and a sequence number.
+ * @param subscriptionState the state of the subscriptions.
*/
public abstract void onVmsSubscriptionChange(VmsSubscriptionState subscriptionState);
@@ -100,9 +99,11 @@
*
* @param layer the layer to publish to.
* @param payload the message to be sent.
+ * @param publisherId the ID that got assigned to the publisher that published the message by
+ * VMS core.
* @return if the call to the method VmsPublisherService.publish was successful.
*/
- public final boolean publish(VmsLayer layer, byte[] payload) {
+ public final void publish(VmsLayer layer, int publisherId, byte[] payload) {
if (DBG) {
Log.d(TAG, "Publishing for layer : " + layer);
}
@@ -110,12 +111,10 @@
IBinder token = getTokenForPublisherServiceThreadSafe();
try {
- mVmsPublisherService.publish(token, layer, payload);
- return true;
+ mVmsPublisherService.publish(token, layer, publisherId, payload);
} catch (RemoteException e) {
Log.e(TAG, "unable to publish message: " + payload, e);
}
- return false;
}
/**
@@ -124,7 +123,7 @@
* @param offering the layers that the publisher may publish.
* @return if the call to VmsPublisherService.setLayersOffering was successful.
*/
- public final boolean setLayersOffering(VmsLayersOffering offering) {
+ public final void setLayersOffering(VmsLayersOffering offering) {
if (DBG) {
Log.d(TAG, "Setting layers offering : " + offering);
}
@@ -133,11 +132,10 @@
try {
mVmsPublisherService.setLayersOffering(token, offering);
- return true;
+ VmsOperationRecorder.get().setLayersOffering(offering);
} catch (RemoteException e) {
Log.e(TAG, "unable to set layers offering: " + offering, e);
}
- return false;
}
private IBinder getTokenForPublisherServiceThreadSafe() {
@@ -155,26 +153,27 @@
return token;
}
- public final int getPublisherStaticId(byte[] publisherInfo) {
+ public final int getPublisherId(byte[] publisherInfo) {
if (mVmsPublisherService == null) {
throw new IllegalStateException("VmsPublisherService not set.");
}
- Integer publisherStaticId = null;
+ Integer publisherId = null;
try {
Log.i(TAG, "Getting publisher static ID");
- publisherStaticId = mVmsPublisherService.getPublisherStaticId(publisherInfo);
+ publisherId = mVmsPublisherService.getPublisherId(publisherInfo);
} catch (RemoteException e) {
Log.e(TAG, "unable to invoke binder method.", e);
}
- if (publisherStaticId == null) {
+ if (publisherId == null) {
throw new IllegalStateException("VmsPublisherService cannot get a publisher static ID.");
+ } else {
+ VmsOperationRecorder.get().getPublisherId(publisherId);
}
- return publisherStaticId;
+ return publisherId;
}
/**
- * Uses the VmsPublisherService binder to get the list of layer/version that have any
- * subscribers.
+ * Uses the VmsPublisherService binder to get the state of the subscriptions.
*
* @return list of layer/version or null in case of error.
*/
diff --git a/car-lib/src/android/car/vms/VmsSubscriberManager.java b/car-lib/src/android/car/vms/VmsSubscriberManager.java
index 84405f4..6f6377c 100644
--- a/car-lib/src/android/car/vms/VmsSubscriberManager.java
+++ b/car-lib/src/android/car/vms/VmsSubscriberManager.java
@@ -26,14 +26,16 @@
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
+
import com.android.internal.annotations.GuardedBy;
+
import java.lang.ref.WeakReference;
import java.util.List;
/**
- * API for interfacing with the VmsSubscriberService. It supports a single listener that can
- * (un)subscribe to different layers. After getting an instance of this manager, the first step
- * must be to call #setListener. After that, #subscribe and #unsubscribe methods can be invoked.
+ * API for interfacing with the VmsSubscriberService. It supports a single client callback that can
+ * (un)subscribe to different layers. Getting notifactions and managing subscriptions is enabled
+ * after setting the client callback with #registerClientCallback.
* SystemApi candidate
*
* @hide
@@ -45,28 +47,33 @@
private final Handler mHandler;
private final IVmsSubscriberService mVmsSubscriberService;
- private final IVmsSubscriberClient mIListener;
- private final Object mListenerLock = new Object();
- @GuardedBy("mListenerLock")
- private VmsSubscriberClientListener mListener;
+ private final IVmsSubscriberClient mSubscriberManagerClient;
+ private final Object mClientCallbackLock = new Object();
+ @GuardedBy("mClientCallbackLock")
+ private VmsSubscriberClientCallback mClientCallback;
- /** Interface exposed to VMS subscribers: it is a wrapper of IVmsSubscriberClient. */
- public interface VmsSubscriberClientListener {
- /** Called when the property is updated */
+ /**
+ * Interface exposed to VMS subscribers: it is a wrapper of IVmsSubscriberClient.
+ */
+ public interface VmsSubscriberClientCallback {
+ /**
+ * Called when the property is updated
+ */
void onVmsMessageReceived(VmsLayer layer, byte[] payload);
- /** Called when layers availability change */
- void onLayersAvailabilityChange(List<VmsLayer> availableLayers);
-
- /** Notifies the client of the disconnect event */
- void onCarDisconnected();
+ /**
+ * Called when layers availability change
+ */
+ void onLayersAvailabilityChanged(List<VmsLayer> availableLayers);
}
/**
* Allows to asynchronously dispatch onVmsMessageReceived events.
*/
private final static class VmsEventHandler extends Handler {
- /** Constants handled in the handler */
+ /**
+ * Constants handled in the handler
+ */
private static final int ON_RECEIVE_MESSAGE_EVENT = 0;
private static final int ON_AVAILABILITY_CHANGE_EVENT = 1;
@@ -88,7 +95,7 @@
// Dispatch the parsed message
mgr.dispatchOnReceiveMessage(vmsDataMessage.getLayer(),
- vmsDataMessage.getPayload());
+ vmsDataMessage.getPayload());
}
break;
case ON_AVAILABILITY_CHANGE_EVENT:
@@ -111,58 +118,80 @@
public VmsSubscriberManager(IBinder service, Handler handler) {
mVmsSubscriberService = IVmsSubscriberService.Stub.asInterface(service);
mHandler = new VmsEventHandler(this, handler.getLooper());
- mIListener = new IVmsSubscriberClient.Stub() {
+ mSubscriberManagerClient = new IVmsSubscriberClient.Stub() {
@Override
public void onVmsMessageReceived(VmsLayer layer, byte[] payload)
- throws RemoteException {
+ throws RemoteException {
// Create the data message
VmsDataMessage vmsDataMessage = new VmsDataMessage(layer, payload);
mHandler.sendMessage(
mHandler.obtainMessage(
- VmsEventHandler.ON_RECEIVE_MESSAGE_EVENT,
- vmsDataMessage));
+ VmsEventHandler.ON_RECEIVE_MESSAGE_EVENT,
+ vmsDataMessage));
}
@Override
- public void onLayersAvailabilityChange(List<VmsLayer> availableLayers) {
+ public void onLayersAvailabilityChanged(List<VmsAssociatedLayer> availableLayers) {
mHandler.sendMessage(
- mHandler.obtainMessage(
- VmsEventHandler.ON_AVAILABILITY_CHANGE_EVENT,
- availableLayers));
+ mHandler.obtainMessage(
+ VmsEventHandler.ON_AVAILABILITY_CHANGE_EVENT,
+ availableLayers));
}
};
}
/**
- * Sets the listener ({@link #mListener}) this manager is linked to. Subscriptions to the
- * {@link com.android.car.VmsSubscriberService} are done through the {@link #mIListener}.
- * Therefore, notifications from the {@link com.android.car.VmsSubscriberService} are received
- * by the {@link #mIListener} and then forwarded to the {@link #mListener}.
+ * Registers the client callback in order to enable communication with the client.
+ * By registering, the client will start getting notifications, and will be able to subscribe
+ * to layers.
+ * <p>
*
- * It is expected that this method is invoked just once during the lifetime of the object.
- *
- * @param listener subscriber listener that will handle onVmsMessageReceived events.
- * @throws IllegalStateException if the listener was already set.
+ * @param clientCallback subscriber callback that will handle onVmsMessageReceived events.
+ * @throws IllegalStateException if the client callback was already set.
*/
- public void setListener(VmsSubscriberClientListener listener) {
- if (DBG) {
- Log.d(TAG, "Setting listener.");
- }
- synchronized (mListenerLock) {
- if (mListener != null) {
- throw new IllegalStateException("Listener is already configured.");
+ public void registerClientCallback(VmsSubscriberClientCallback clientCallback)
+ throws CarNotConnectedException {
+ synchronized (mClientCallbackLock) {
+ if (mClientCallback != null) {
+ throw new IllegalStateException("Client callback is already configured.");
}
- mListener = listener;
+ mClientCallback = clientCallback;
+ }
+ try {
+ mVmsSubscriberService.addVmsSubscriberToNotifications(mSubscriberManagerClient);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not connect: ", e);
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ * Unregisters the client callback which disables communication with the client.
+ * @throws CarNotConnectedException, IllegalStateException
+ */
+ public void unregisterClientCallback()
+ throws CarNotConnectedException {
+
+ try {
+ mVmsSubscriberService.removeVmsSubscriberToNotifications(mSubscriberManagerClient);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not connect: ", e);
+ throw new CarNotConnectedException(e);
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Could not unsubscribe from notifications");
+ throw e;
+ }
+
+ synchronized (mClientCallbackLock) {
+ mClientCallback = null;
}
}
/**
* Returns a serialized publisher information for a publisher ID.
*/
- public byte[] getPublisherInfo(int publisherId) throws CarNotConnectedException, IllegalStateException {
- if (DBG) {
- Log.d(TAG, "Getting all publishers info.");
- }
+ public byte[] getPublisherInfo(int publisherId)
+ throws CarNotConnectedException, IllegalStateException {
try {
return mVmsSubscriberService.getPublisherInfo(publisherId);
} catch (RemoteException e) {
@@ -178,23 +207,14 @@
* Subscribes to listen to the layer specified.
*
* @param layer the layer to subscribe to.
- * @throws IllegalStateException if the listener was not set via {@link #setListener}.
+ * @throws IllegalStateException if the client callback was not set via
+ * {@link #registerClientCallback}.
*/
public void subscribe(VmsLayer layer) throws CarNotConnectedException {
- if (DBG) {
- Log.d(TAG, "Subscribing to layer: " + layer);
- }
- VmsSubscriberClientListener listener;
- synchronized (mListenerLock) {
- listener = mListener;
- }
- if (listener == null) {
- Log.w(TAG, "subscribe: listener was not set, " +
- "setListener must be called first.");
- throw new IllegalStateException("Listener was not set.");
- }
+ verifySubscriptionIsAllowed();
try {
- mVmsSubscriberService.addVmsSubscriberClientListener(mIListener, layer);
+ mVmsSubscriberService.addVmsSubscriber(mSubscriberManagerClient, layer);
+ VmsOperationRecorder.get().subscribe(layer);
} catch (RemoteException e) {
Log.e(TAG, "Could not connect: ", e);
throw new CarNotConnectedException(e);
@@ -203,21 +223,33 @@
}
}
- public void subscribeAll() throws CarNotConnectedException {
- if (DBG) {
- Log.d(TAG, "Subscribing passively to all data messages");
- }
- VmsSubscriberClientListener listener;
- synchronized (mListenerLock) {
- listener = mListener;
- }
- if (listener == null) {
- Log.w(TAG, "subscribe: listener was not set, " +
- "setListener must be called first.");
- throw new IllegalStateException("Listener was not set.");
- }
+ /**
+ * Subscribes to listen to the layer specified from the publisher specified.
+ *
+ * @param layer the layer to subscribe to.
+ * @param publisherId the publisher of the layer.
+ * @throws IllegalStateException if the client callback was not set via
+ * {@link #registerClientCallback}.
+ */
+ public void subscribe(VmsLayer layer, int publisherId) throws CarNotConnectedException {
+ verifySubscriptionIsAllowed();
try {
- mVmsSubscriberService.addVmsSubscriberClientPassiveListener(mIListener);
+ mVmsSubscriberService.addVmsSubscriberToPublisher(
+ mSubscriberManagerClient, layer, publisherId);
+ VmsOperationRecorder.get().subscribe(layer, publisherId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not connect: ", e);
+ throw new CarNotConnectedException(e);
+ } catch (IllegalStateException ex) {
+ Car.checkCarNotConnectedExceptionFromCarService(ex);
+ }
+ }
+
+ public void startMonitoring() throws CarNotConnectedException {
+ verifySubscriptionIsAllowed();
+ try {
+ mVmsSubscriberService.addVmsSubscriberPassive(mSubscriberManagerClient);
+ VmsOperationRecorder.get().startMonitoring();
} catch (RemoteException e) {
Log.e(TAG, "Could not connect: ", e);
throw new CarNotConnectedException(e);
@@ -229,24 +261,15 @@
/**
* Unsubscribes from the layer/version specified.
*
- * @param layer the layer to unsubscribe from.
- * @throws IllegalStateException if the listener was not set via {@link #setListener}.
+ * @param layer the layer to unsubscribe from.
+ * @throws IllegalStateException if the client callback was not set via
+ * {@link #registerClientCallback}.
*/
public void unsubscribe(VmsLayer layer) {
- if (DBG) {
- Log.d(TAG, "Unsubscribing from layer: " + layer);
- }
- VmsSubscriberClientListener listener;
- synchronized (mListenerLock) {
- listener = mListener;
- }
- if (listener == null) {
- Log.w(TAG, "unsubscribe: listener was not set, " +
- "setListener must be called first.");
- throw new IllegalStateException("Listener was not set.");
- }
+ verifySubscriptionIsAllowed();
try {
- mVmsSubscriberService.removeVmsSubscriberClientListener(mIListener, layer);
+ mVmsSubscriberService.removeVmsSubscriber(mSubscriberManagerClient, layer);
+ VmsOperationRecorder.get().unsubscribe(layer);
} catch (RemoteException e) {
Log.e(TAG, "Failed to unregister subscriber", e);
// ignore
@@ -255,21 +278,31 @@
}
}
- public void unsubscribeAll() {
- if (DBG) {
- Log.d(TAG, "Unsubscribing passively from all data messages");
- }
- VmsSubscriberClientListener listener;
- synchronized (mListenerLock) {
- listener = mListener;
- }
- if (listener == null) {
- Log.w(TAG, "unsubscribeAll: listener was not set, " +
- "setListener must be called first.");
- throw new IllegalStateException("Listener was not set.");
- }
+ /**
+ * Unsubscribes from the layer/version specified.
+ *
+ * @param layer the layer to unsubscribe from.
+ * @param publisherId the pubisher of the layer.
+ * @throws IllegalStateException if the client callback was not set via
+ * {@link #registerClientCallback}.
+ */
+ public void unsubscribe(VmsLayer layer, int publisherId) {
try {
- mVmsSubscriberService.removeVmsSubscriberClientPassiveListener(mIListener);
+ mVmsSubscriberService.removeVmsSubscriberToPublisher(
+ mSubscriberManagerClient, layer, publisherId);
+ VmsOperationRecorder.get().unsubscribe(layer, publisherId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to unregister subscriber", e);
+ // ignore
+ } catch (IllegalStateException ex) {
+ Car.hideCarNotConnectedExceptionFromCarService(ex);
+ }
+ }
+
+ public void stopMonitoring() {
+ try {
+ mVmsSubscriberService.removeVmsSubscriberPassive(mSubscriberManagerClient);
+ VmsOperationRecorder.get().stopMonitoring();
} catch (RemoteException e) {
Log.e(TAG, "Failed to unregister subscriber ", e);
// ignore
@@ -279,41 +312,49 @@
}
private void dispatchOnReceiveMessage(VmsLayer layer, byte[] payload) {
- VmsSubscriberClientListener listener;
- synchronized (mListenerLock) {
- listener = mListener;
- }
- if (listener == null) {
- Log.e(TAG, "Listener died, not dispatching event.");
+ VmsSubscriberClientCallback clientCallback = getClientCallbackThreadSafe();
+ if (clientCallback == null) {
+ Log.e(TAG, "Cannot dispatch received message.");
return;
}
- listener.onVmsMessageReceived(layer, payload);
+ clientCallback.onVmsMessageReceived(layer, payload);
}
private void dispatchOnAvailabilityChangeMessage(List<VmsLayer> availableLayers) {
- VmsSubscriberClientListener listener;
- synchronized (mListenerLock) {
- listener = mListener;
- }
- if (listener == null) {
- Log.e(TAG, "Listener died, not dispatching event.");
+ VmsSubscriberClientCallback clientCallback = getClientCallbackThreadSafe();
+ if (clientCallback == null) {
+ Log.e(TAG, "Cannot dispatch availability change message.");
return;
}
- listener.onLayersAvailabilityChange(availableLayers);
+ clientCallback.onLayersAvailabilityChanged(availableLayers);
}
- /** @hide */
+ private VmsSubscriberClientCallback getClientCallbackThreadSafe() {
+ VmsSubscriberClientCallback clientCallback;
+ synchronized (mClientCallbackLock) {
+ clientCallback = mClientCallback;
+ }
+ if (clientCallback == null) {
+ Log.e(TAG, "client callback not set.");
+ }
+ return clientCallback;
+ }
+
+ /*
+ * Verifies that the subscriber is in a state where it is allowed to subscribe.
+ */
+ private void verifySubscriptionIsAllowed() {
+ VmsSubscriberClientCallback clientCallback = getClientCallbackThreadSafe();
+ if (clientCallback == null) {
+ throw new IllegalStateException("Cannot subscribe.");
+ }
+ }
+
+ /**
+ * @hide
+ */
@Override
public void onCarDisconnected() {
- VmsSubscriberClientListener listener;
- synchronized (mListenerLock) {
- listener = mListener;
- }
- if (listener == null) {
- Log.e(TAG, "Listener died, not dispatching event.");
- return;
- }
- listener.onCarDisconnected();
}
private static final class VmsDataMessage {
@@ -328,6 +369,7 @@
public VmsLayer getLayer() {
return mLayer;
}
+
public byte[] getPayload() {
return mPayload;
}
diff --git a/car-lib/src/android/car/vms/VmsSubscriptionState.java b/car-lib/src/android/car/vms/VmsSubscriptionState.java
index 0e36fb1..ea433f7 100644
--- a/car-lib/src/android/car/vms/VmsSubscriptionState.java
+++ b/car-lib/src/android/car/vms/VmsSubscriptionState.java
@@ -19,9 +19,13 @@
import android.car.annotation.FutureFeature;
import android.os.Parcel;
import android.os.Parcelable;
+
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
+
/**
* The list of layers with subscribers.
@@ -31,30 +35,48 @@
@FutureFeature
public final class VmsSubscriptionState implements Parcelable {
private final int mSequenceNumber;
- private final List<VmsLayer> mLayers;
+ private final Set<VmsLayer> mLayers;
+ private final Set<VmsAssociatedLayer> mSubscribedLayersFromPublishers;
/**
- * Construct a dependency for layer on other layers.
+ * Construcs a summary of the state of the current subscriptions for publishers to consume
+ * and adjust which layers that the are publishing.
*/
- public VmsSubscriptionState(int sequenceNumber, List<VmsLayer> dependencies) {
+ public VmsSubscriptionState(int sequenceNumber,
+ Set<VmsLayer> subscribedLayers,
+ Set<VmsAssociatedLayer> layersFromPublishers) {
mSequenceNumber = sequenceNumber;
- mLayers = Collections.unmodifiableList(dependencies);
+ mLayers = Collections.unmodifiableSet(subscribedLayers);
+ mSubscribedLayersFromPublishers = Collections.unmodifiableSet(layersFromPublishers);
}
+ /**
+ * Returns the sequence number assigned by the VMS service. Sequence numbers are
+ * monotonically increasing and help clients ignore potential out-of-order states.
+ */
public int getSequenceNumber() {
return mSequenceNumber;
}
- public List<VmsLayer> getLayers() {
+ public Set<VmsLayer> getLayers() {
return mLayers;
}
+ public Set<VmsAssociatedLayer> getAssociatedLayers() {
+ return mSubscribedLayersFromPublishers;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("sequence number=").append(mSequenceNumber);
sb.append("; layers={");
- for(VmsLayer layer : mLayers) {
+ for (VmsLayer layer : mLayers) {
+ sb.append(layer).append(",");
+ }
+ sb.append("}");
+ sb.append("; associatedLayers={");
+ for (VmsAssociatedLayer layer : mSubscribedLayersFromPublishers) {
sb.append(layer).append(",");
}
sb.append("}");
@@ -62,19 +84,21 @@
}
public static final Parcelable.Creator<VmsSubscriptionState> CREATOR = new
- Parcelable.Creator<VmsSubscriptionState>() {
- public VmsSubscriptionState createFromParcel(Parcel in) {
- return new VmsSubscriptionState(in);
- }
- public VmsSubscriptionState[] newArray(int size) {
- return new VmsSubscriptionState[size];
- }
- };
+ Parcelable.Creator<VmsSubscriptionState>() {
+ public VmsSubscriptionState createFromParcel(Parcel in) {
+ return new VmsSubscriptionState(in);
+ }
+
+ public VmsSubscriptionState[] newArray(int size) {
+ return new VmsSubscriptionState[size];
+ }
+ };
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mSequenceNumber);
- out.writeParcelableList(mLayers, flags);
+ out.writeParcelableList(new ArrayList(mLayers), flags);
+ out.writeParcelableList(new ArrayList(mSubscribedLayersFromPublishers), flags);
}
@Override
@@ -84,8 +108,13 @@
private VmsSubscriptionState(Parcel in) {
mSequenceNumber = in.readInt();
+
List<VmsLayer> layers = new ArrayList<>();
in.readParcelableList(layers, VmsLayer.class.getClassLoader());
- mLayers = Collections.unmodifiableList(layers);
+ mLayers = Collections.unmodifiableSet(new HashSet(layers));
+
+ List<VmsAssociatedLayer> associatedLayers = new ArrayList<>();
+ in.readParcelableList(associatedLayers, VmsAssociatedLayer.class.getClassLoader());
+ mSubscribedLayersFromPublishers = Collections.unmodifiableSet(new HashSet(associatedLayers));
}
}
\ No newline at end of file
diff --git a/car-maps-placeholder/Android.mk b/car-maps-placeholder/Android.mk
index 5f0b93a..53cdbf8 100644
--- a/car-maps-placeholder/Android.mk
+++ b/car-maps-placeholder/Android.mk
@@ -14,6 +14,8 @@
# limitations under the License.
#
+ifneq ($(TARGET_BUILD_PDK), true)
+
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
@@ -35,3 +37,5 @@
include packages/apps/Car/libs/car-stream-ui-lib/car-stream-ui-lib.mk
include $(BUILD_PACKAGE)
+
+endif
diff --git a/car-support-lib/Android.mk b/car-support-lib/Android.mk
index d0052e4..76f2587 100644
--- a/car-support-lib/Android.mk
+++ b/car-support-lib/Android.mk
@@ -19,7 +19,8 @@
LOCAL_PATH:= $(call my-dir)
-#Build prebuilt android.support.car library
+# Build prebuilt android.support.car library
+# ---------------------------------------------
include $(CLEAR_VARS)
LOCAL_AAPT_FLAGS := --auto-add-overlay
@@ -47,7 +48,36 @@
include $(BUILD_STATIC_JAVA_LIBRARY)
ifeq ($(BOARD_IS_AUTOMOTIVE), true)
- $(call dist-for-goals,dist_files,$(built_aar):android.support.car.aar)
+ $(call dist-for-goals, dist_files, $(built_aar):android.support.car.aar)
+endif
+
+# Same as above, except without proguard.
+# ---------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_AAPT_FLAGS := --auto-add-overlay
+LOCAL_MODULE := android.support.car-1p-prebuilt
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+# Build against the current public APIs of the SDK
+LOCAL_SDK_VERSION := current
+
+LOCAL_MANIFEST_FILE := AndroidManifest.xml
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)
+LOCAL_JAVA_LIBRARIES += android.car\
+ android-support-annotations
+# Specify 1.7 for backwards compatibility.
+# Otherwise the lib won't be usable on pre-N devices
+LOCAL_JAVA_LANGUAGE_VERSION := 1.7
+
+LOCAL_PROGUARD_ENABLED := disabled
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+ifeq ($(BOARD_IS_AUTOMOTIVE), true)
+ $(call dist-for-goals, dist_files, $(built_aar):android.support.car-1p.aar)
endif
# Build support library.
diff --git a/car-support-lib/api/current.txt b/car-support-lib/api/current.txt
index 8912265..3d9c32b 100644
--- a/car-support-lib/api/current.txt
+++ b/car-support-lib/api/current.txt
@@ -100,8 +100,14 @@
field public static final int INDEX_COMPASS_BEARING = 0; // 0x0
field public static final int INDEX_COMPASS_PITCH = 1; // 0x1
field public static final int INDEX_COMPASS_ROLL = 2; // 0x2
+ field public static final int INDEX_WHEEL_DISTANCE_FRONT_LEFT = 1; // 0x1
+ field public static final int INDEX_WHEEL_DISTANCE_FRONT_RIGHT = 2; // 0x2
+ field public static final int INDEX_WHEEL_DISTANCE_REAR_LEFT = 4; // 0x4
+ field public static final int INDEX_WHEEL_DISTANCE_REAR_RIGHT = 3; // 0x3
+ field public static final int INDEX_WHEEL_DISTANCE_RESET_COUNT = 0; // 0x0
field public final float[] floatValues;
field public final int[] intValues;
+ field public final long[] longValues;
field public final int sensorType;
field public final long timestamp;
}
@@ -144,10 +150,13 @@
method public abstract void removeListener(android.support.car.hardware.CarSensorManager.OnSensorChangedListener, int);
field public static final int SENSOR_RATE_FASTEST = 0; // 0x0
field public static final int SENSOR_RATE_NORMAL = 3; // 0x3
+ field public static final int SENSOR_TYPE_ABS_ACTIVE = 24; // 0x18
field public static final int SENSOR_TYPE_COMPASS = 1; // 0x1
field public static final int SENSOR_TYPE_DRIVING_STATUS = 11; // 0xb
field public static final int SENSOR_TYPE_NIGHT = 9; // 0x9
field public static final int SENSOR_TYPE_PARKING_BRAKE = 6; // 0x6
+ field public static final int SENSOR_TYPE_TRACTION_CONTROL_ACTIVE = 25; // 0x19
+ field public static final int SENSOR_TYPE_WHEEL_TICK_DISTANCE = 23; // 0x17
}
public static abstract interface CarSensorManager.OnSensorChangedListener {
@@ -211,9 +220,9 @@
ctor public CarNavigationStatusManager();
method public abstract void addListener(android.support.car.navigation.CarNavigationStatusManager.CarNavigationCallback) throws android.support.car.CarNotConnectedException;
method public abstract void removeListener();
- method public abstract void sendNavigationStatus(int) throws android.support.car.CarNotConnectedException;
- method public abstract void sendNavigationTurnDistanceEvent(int, int, int, int) throws android.support.car.CarNotConnectedException;
- method public abstract void sendNavigationTurnEvent(int, java.lang.CharSequence, int, int, int) throws android.support.car.CarNotConnectedException;
+ method public abstract deprecated void sendNavigationStatus(int) throws android.support.car.CarNotConnectedException;
+ method public abstract deprecated void sendNavigationTurnDistanceEvent(int, int, int, int) throws android.support.car.CarNotConnectedException;
+ method public abstract deprecated void sendNavigationTurnEvent(int, java.lang.CharSequence, int, int, int) throws android.support.car.CarNotConnectedException;
field public static final int DISTANCE_FEET = 4; // 0x4
field public static final int DISTANCE_KILOMETERS = 2; // 0x2
field public static final int DISTANCE_METERS = 1; // 0x1
diff --git a/car-support-lib/proguard-release.flags b/car-support-lib/proguard-release.flags
index daccb0d..6ec8b3c 100644
--- a/car-support-lib/proguard-release.flags
+++ b/car-support-lib/proguard-release.flags
@@ -5227,8 +5227,14 @@
public static int INDEX_COMPASS_BEARING;
public static int INDEX_COMPASS_PITCH;
public static int INDEX_COMPASS_ROLL;
+ public static int INDEX_WHEEL_DISTANCE_FRONT_LEFT;
+ public static int INDEX_WHEEL_DISTANCE_FRONT_RIGHT;
+ public static int INDEX_WHEEL_DISTANCE_REAR_LEFT;
+ public static int INDEX_WHEEL_DISTANCE_REAR_RIGHT;
+ public static int INDEX_WHEEL_DISTANCE_RESET_COUNT;
public float[] floatValues;
public int[] intValues;
+ public long[] longValues;
public int sensorType;
public long timestamp;
}
@@ -5286,10 +5292,13 @@
public static int SENSOR_RATE_FASTEST;
public static int SENSOR_RATE_NORMAL;
+ public static int SENSOR_TYPE_ABS_ACTIVE;
public static int SENSOR_TYPE_COMPASS;
public static int SENSOR_TYPE_DRIVING_STATUS;
public static int SENSOR_TYPE_NIGHT;
public static int SENSOR_TYPE_PARKING_BRAKE;
+ public static int SENSOR_TYPE_TRACTION_CONTROL_ACTIVE;
+ public static int SENSOR_TYPE_WHEEL_TICK_DISTANCE;
}
-keep class android.support.car.hardware.CarSensorManager$OnSensorChangedListener {
diff --git a/car-support-lib/src/android/support/car/Car.java b/car-support-lib/src/android/support/car/Car.java
index c47b650..a374d87 100644
--- a/car-support-lib/src/android/support/car/Car.java
+++ b/car-support-lib/src/android/support/car/Car.java
@@ -178,6 +178,12 @@
*/
public static final String PERMISSION_SPEED = "android.car.permission.CAR_SPEED";
/**
+ * Permission necessary to access car dynamics state.
+ * @hide
+ */
+ public static final String PERMISSION_VEHICLE_DYNAMICS_STATE =
+ "android.car.permission.VEHICLE_DYNAMICS_STATE";
+ /**
* Permission necessary to access a car-specific communication channel.
*/
public static final String PERMISSION_VENDOR_EXTENSION =
diff --git a/car-support-lib/src/android/support/car/hardware/CarSensorConfig.java b/car-support-lib/src/android/support/car/hardware/CarSensorConfig.java
new file mode 100644
index 0000000..3c71e04
--- /dev/null
+++ b/car-support-lib/src/android/support/car/hardware/CarSensorConfig.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.car.hardware;
+
+import android.os.Bundle;
+import android.support.annotation.RestrictTo;
+import java.util.ArrayList;
+
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+
+/**
+ * A CarSensorConfig object corresponds to a single sensor type coming from the car.
+ * @hide
+ */
+public class CarSensorConfig {
+ /** List of property specific mapped elements in bundle for WHEEL_TICK_DISTANCE sensor*/
+ /** @hide */
+ public final static String WHEEL_TICK_DISTANCE_SUPPORTED_WHEELS =
+ "android.car.wheelTickDistanceSupportedWhheels";
+ /** @hide */
+ public final static String WHEEL_TICK_DISTANCE_FRONT_LEFT_UM_PER_TICK =
+ "android.car.wheelTickDistanceFrontLeftUmPerTick";
+ /** @hide */
+ public final static String WHEEL_TICK_DISTANCE_FRONT_RIGHT_UM_PER_TICK =
+ "android.car.wheelTickDistanceFrontRightUmPerTick";
+ /** @hide */
+ public final static String WHEEL_TICK_DISTANCE_REAR_RIGHT_UM_PER_TICK =
+ "android.car.wheelTickDistanceRearRightUmPerTick";
+ /** @hide */
+ public final static String WHEEL_TICK_DISTANCE_REAR_LEFT_UM_PER_TICK =
+ "android.car.wheelTickDistanceRearLeftUmPerTick";
+
+ /** Config data stored in Bundle */
+ private final Bundle mConfig;
+ private final int mType;
+
+ private final static int RAW_BUNDLE_SIZE = 4;
+ private final static int WHEEL_TICK_DISTANCE_BUNDLE_SIZE = 6;
+
+ /**
+ * Constructs a {@link CarSensorConfig}. Handled by CarSensorManager implementations.
+ * App developers need not worry about constructing these objects.
+ * @hide
+ */
+ @RestrictTo(GROUP_ID)
+ public CarSensorConfig(int type, Bundle in) {
+ mType = type;
+ mConfig = in.deepCopy();
+ }
+
+ private void checkType(int type) {
+ if (mType == type) {
+ return;
+ }
+ throw new UnsupportedOperationException(String.format(
+ "Invalid sensor type: expected %d, got %d", type, mType));
+ }
+
+ /** @hide */
+ public int getInt(String key) {
+ if (mConfig.containsKey(key)) {
+ return mConfig.getInt(key);
+ } else {
+ throw new IllegalArgumentException("SensorType " + mType +
+ " does not contain key: " + key);
+ }
+ }
+
+ /** @hide */
+ public int getType() {
+ return mType;
+ }
+
+ /** @hide */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(getClass().getName() + "[");
+ sb.append("mConfig: " + mConfig.toString());
+ sb.append("mType: " + mType);
+ sb.append("]");
+ return sb.toString();
+ }
+}
diff --git a/car-support-lib/src/android/support/car/hardware/CarSensorEvent.java b/car-support-lib/src/android/support/car/hardware/CarSensorEvent.java
index 215cddf..f7b4dba 100644
--- a/car-support-lib/src/android/support/car/hardware/CarSensorEvent.java
+++ b/car-support-lib/src/android/support/car/hardware/CarSensorEvent.java
@@ -88,6 +88,15 @@
public static final int INDEX_COMPASS_ROLL = 2;
+ /**
+ * Index for {@link CarSensorManager#SENSOR_TYPE_WHEEL_TICK_DISTANCE} in longValues.
+ */
+ public static final int INDEX_WHEEL_DISTANCE_RESET_COUNT = 0;
+ public static final int INDEX_WHEEL_DISTANCE_FRONT_LEFT = 1;
+ public static final int INDEX_WHEEL_DISTANCE_FRONT_RIGHT = 2;
+ public static final int INDEX_WHEEL_DISTANCE_REAR_RIGHT = 3;
+ public static final int INDEX_WHEEL_DISTANCE_REAR_LEFT = 4;
+
private static final long MILLI_IN_NANOS = 1000000L;
/** Sensor type for this event, such as {@link CarSensorManager#SENSOR_TYPE_COMPASS}. */
@@ -104,9 +113,12 @@
public final float[] floatValues;
/** Array holding int type of sensor data. */
public final int[] intValues;
+ /** array holding long int type of sensor data */
+ public final long[] longValues;
private static final float[] EMPTY_FLOAT_ARRAY = {};
private static final int[] EMPTY_INT_ARRAY = {};
+ private static final long[] EMPTY_LONG_ARRAY = {};
/**
* Constructs a {@link CarSensorEvent} from integer values. Handled by
@@ -115,11 +127,13 @@
* @hide
*/
@RestrictTo(GROUP_ID)
- public CarSensorEvent(int sensorType, long timestamp, int floatValueSize, int intValueSize) {
+ public CarSensorEvent(int sensorType, long timestamp, int floatValueSize, int intValueSize,
+ int longValueSize) {
this.sensorType = sensorType;
this.timestamp = timestamp;
floatValues = new float[floatValueSize];
intValues = new int[intValueSize];
+ longValues = new long[longValueSize];
}
/**
@@ -128,14 +142,17 @@
* @param timestamp time since system start in nanoseconds
* @param floatValues {@code null} will be converted to an empty array
* @param intValues {@code null} will be converted to an empty array
+ * @param longValues {@code null} will be converted to an empty array
* @hide
*/
@RestrictTo(GROUP_ID)
- public CarSensorEvent(int sensorType, long timestamp, float[] floatValues, int[] intValues) {
+ public CarSensorEvent(int sensorType, long timestamp, float[] floatValues, int[] intValues,
+ long[] longValues) {
this.sensorType = sensorType;
this.timestamp = timestamp;
this.floatValues = (floatValues == null) ? EMPTY_FLOAT_ARRAY : floatValues;
this.intValues = (intValues == null) ? EMPTY_INT_ARRAY : intValues;
+ this.longValues = (longValues == null) ? EMPTY_LONG_ARRAY : longValues;
}
/**
@@ -145,10 +162,12 @@
* @param floatValues {@code null} will be converted to an empty array
* @param byteValues bytes will be converted into the intValues array. {@code null} will be
* converted to an empty array.
+ * @param longValues {@code null} will be converted to an empty array
* @hide
*/
@RestrictTo(GROUP_ID)
- public CarSensorEvent(int sensorType, long timestamp, float[] floatValues, byte[] byteValues) {
+ public CarSensorEvent(int sensorType, long timestamp, float[] floatValues, byte[] byteValues,
+ long[] longValues) {
this.sensorType = sensorType;
this.timestamp = timestamp;
this.floatValues = (floatValues == null) ? EMPTY_FLOAT_ARRAY : floatValues;
@@ -160,6 +179,7 @@
this.intValues[i] = byteValues[i];
}
}
+ this.longValues = (longValues == null) ? EMPTY_LONG_ARRAY : longValues;
}
private void checkType(int type) {
@@ -920,6 +940,104 @@
}
/** @hide */
+ public static class CarWheelTickDistanceData {
+ public final long timestamp;
+ public final long sensorResetCount;
+ public final long frontLeftWheelDistanceMm;
+ public final long frontRightWheelDistanceMm;
+ public final long rearRightWheelDistanceMm;
+ public final long rearLeftWheelDistanceMm;
+
+ /** @hide */
+ @RestrictTo(GROUP_ID)
+ public CarWheelTickDistanceData(long timestamp, long sensorResetCount,
+ long frontLeftWheelDistanceMm, long frontRightWheelDistanceMm,
+ long rearRightWheelDistanceMm, long rearLeftWheelDistanceMm) {
+ this.timestamp = timestamp;
+ this.sensorResetCount = sensorResetCount;
+ this.frontLeftWheelDistanceMm = frontLeftWheelDistanceMm;
+ this.frontRightWheelDistanceMm = frontRightWheelDistanceMm;
+ this.rearRightWheelDistanceMm = rearRightWheelDistanceMm;
+ this.rearLeftWheelDistanceMm = rearLeftWheelDistanceMm;
+ }
+ }
+
+ /**
+ * Convenience method for obtaining a {@link CarWheelTickDistanceData} object from a
+ * CarSensorEvent object with type {@link CarSensorManager#SENSOR_TYPE_WHEEL_TICK_DISTANCE}.
+ *
+ * @return CarWheelTickDistanceData object corresponding to data contained in the CarSensorEvent
+ * @hide
+ */
+ public CarWheelTickDistanceData getCarWheelTickDistanceData() {
+ checkType(CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE);
+ long sensorResetCount = longValues[INDEX_WHEEL_DISTANCE_RESET_COUNT];
+ long frontLeftWheelDistanceMm = longValues[INDEX_WHEEL_DISTANCE_FRONT_LEFT];
+ long frontRightWheelDistanceMm = longValues[INDEX_WHEEL_DISTANCE_FRONT_RIGHT];
+ long rearRightWheelDistanceMm = longValues[INDEX_WHEEL_DISTANCE_REAR_RIGHT];
+ long rearLeftWheelDistanceMm = longValues[INDEX_WHEEL_DISTANCE_REAR_LEFT];
+ return new CarWheelTickDistanceData(timestamp, sensorResetCount, frontLeftWheelDistanceMm,
+ frontRightWheelDistanceMm, rearRightWheelDistanceMm, rearLeftWheelDistanceMm);
+ }
+
+ /** @hide */
+ public static class CarAbsActiveData {
+ public final long timestamp;
+ public final boolean absIsActive;
+
+ /** @hide */
+ @RestrictTo(GROUP_ID)
+ public CarAbsActiveData(long timestamp, boolean absIsActive) {
+ this.timestamp = timestamp;
+ this.absIsActive = absIsActive;
+ };
+ }
+
+ /**
+ * Convenience method for obtaining a {@link CarAbsActiveData} object from a CarSensorEvent
+ * object with type {@link CarSensorManager#SENSOR_TYPE_ABS_ACTIVE}.
+ *
+ * @param data an optional output parameter which, if non-null, will be used by this method
+ * instead of a newly created object.
+ * @return a CarAbsActiveData object corresponding to data contained in the CarSensorEvent.
+ * @hide
+ */
+ public CarAbsActiveData getCarAbsActiveData() {
+ checkType(CarSensorManager.SENSOR_TYPE_ABS_ACTIVE);
+ boolean absIsActive = intValues[0] == 1;
+ return new CarAbsActiveData(timestamp, absIsActive);
+ }
+
+ /** @hide */
+ public static class CarTractionControlActiveData {
+ public final long timestamp;
+ public final boolean tractionControlIsActive;
+
+ /** @hide */
+ @RestrictTo(GROUP_ID)
+ public CarTractionControlActiveData(long timestamp, boolean tractionControlIsActive) {
+ this.timestamp = timestamp;
+ this.tractionControlIsActive = tractionControlIsActive;
+ };
+ }
+
+ /**
+ * Convenience method for obtaining a {@link CarTractionControlActiveData} object from a
+ * CarSensorEvent object with type {@link CarSensorManager#SENSOR_TYPE_TRACTION_CONTROL_ACTIVE}.
+ *
+ * @param data an optional output parameter which, if non-null, will be used by this method
+ * instead of a newly created object.
+ * @return a CarTractionControlActiveData object corresponding to data contained in the
+ * CarSensorEvent.
+ * @hide
+ */
+ public CarTractionControlActiveData getCarTractionControlActiveData() {
+ checkType(CarSensorManager.SENSOR_TYPE_TRACTION_CONTROL_ACTIVE);
+ boolean tractionControlIsActive = intValues[0] == 1;
+ return new CarTractionControlActiveData(timestamp, tractionControlIsActive);
+ }
+
+ /** @hide */
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -937,6 +1055,12 @@
sb.append(" " + v);
}
}
+ if (longValues != null && longValues.length > 0) {
+ sb.append(" long values:");
+ for (long v: longValues) {
+ sb.append(" " + v);
+ }
+ }
sb.append("]");
return sb.toString();
}
diff --git a/car-support-lib/src/android/support/car/hardware/CarSensorManager.java b/car-support-lib/src/android/support/car/hardware/CarSensorManager.java
index adc976c..8bddce1 100644
--- a/car-support-lib/src/android/support/car/hardware/CarSensorManager.java
+++ b/car-support-lib/src/android/support/car/hardware/CarSensorManager.java
@@ -124,6 +124,23 @@
public static final int SENSOR_TYPE_RESERVED21 = 21;
/** @hide */
public static final int SENSOR_TYPE_RESERVED22 = 22;
+ /**
+ * Represents wheel distance in millimeters. Some cars may not have individual sensors on each
+ * wheel. If a value is not available, -1 will be reported. The wheel distance accumulates
+ * over time.
+ * Requires {@link Car#PERMISSION_MILEAGE} permission.
+ */
+ public static final int SENSOR_TYPE_WHEEL_TICK_DISTANCE = 23;
+ /**
+ * Set to true when ABS is active. This sensor is event driven.
+ * Requires {@link Car#PERMISSION_VEHICLE_DYNAMICS_STATE} permission.
+ */
+ public static final int SENSOR_TYPE_ABS_ACTIVE = 24;
+ /**
+ * Set to true when traction control is active. This sensor is event driven.
+ * Requires {@link Car#PERMISSION_VEHICLE_DYNAMICS_STATE} permission.
+ */
+ public static final int SENSOR_TYPE_TRACTION_CONTROL_ACTIVE = 25;
/**
* Sensors defined in this range [{@link #SENSOR_TYPE_VENDOR_EXTENSION_START},
@@ -155,7 +172,10 @@
SENSOR_TYPE_ENVIRONMENT,
SENSOR_TYPE_ACCELEROMETER,
SENSOR_TYPE_GPS_SATELLITE,
- SENSOR_TYPE_GYROSCOPE
+ SENSOR_TYPE_GYROSCOPE,
+ SENSOR_TYPE_WHEEL_TICK_DISTANCE,
+ SENSOR_TYPE_ABS_ACTIVE,
+ SENSOR_TYPE_TRACTION_CONTROL_ACTIVE
})
@Retention(RetentionPolicy.SOURCE)
public @interface SensorType {}
@@ -230,7 +250,8 @@
* @throws SecurityException if missing the appropriate permission.
*/
@RequiresPermission(anyOf={Manifest.permission.ACCESS_FINE_LOCATION, Car.PERMISSION_SPEED,
- Car.PERMISSION_MILEAGE, Car.PERMISSION_FUEL}, conditional=true)
+ Car.PERMISSION_MILEAGE, Car.PERMISSION_FUEL, Car.PERMISSION_VEHICLE_DYNAMICS_STATE},
+ conditional=true)
public abstract boolean addListener(OnSensorChangedListener listener,
@SensorType int sensorType, @SensorRate int rate)
throws CarNotConnectedException, IllegalArgumentException;
@@ -259,4 +280,15 @@
*/
public abstract CarSensorEvent getLatestSensorEvent(@SensorType int type)
throws CarNotConnectedException;
+
+ /**
+ * Get the config data for the given type.
+ * @param sensor type to request
+ * @return CarSensorConfig object
+ * @throws CarNotConnectedException if the connection to the car service has been lost.
+ * @hide
+ */
+ public abstract CarSensorConfig getSensorConfig(@SensorType int type)
+ throws CarNotConnectedException;
+
}
diff --git a/car-support-lib/src/android/support/car/hardware/CarSensorManagerEmbedded.java b/car-support-lib/src/android/support/car/hardware/CarSensorManagerEmbedded.java
index 2692267..0fde10f 100644
--- a/car-support-lib/src/android/support/car/hardware/CarSensorManagerEmbedded.java
+++ b/car-support-lib/src/android/support/car/hardware/CarSensorManagerEmbedded.java
@@ -155,6 +155,16 @@
}
@Override
+ public CarSensorConfig getSensorConfig(@SensorType int type)
+ throws CarNotConnectedException {
+ try {
+ return convert(mManager.getSensorConfig(type));
+ } catch (android.car.CarNotConnectedException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ @Override
public void onCarDisconnected() {
//nothing to do
}
@@ -173,7 +183,14 @@
return null;
}
return new CarSensorEvent(event.sensorType, event.timestamp, event.floatValues,
- event.intValues);
+ event.intValues, event.longValues);
+ }
+
+ private static CarSensorConfig convert(android.car.hardware.CarSensorConfig cfg) {
+ if (cfg == null) {
+ return null;
+ }
+ return new CarSensorConfig(cfg.getType(), cfg.getBundle());
}
private static class OnSensorChangedListenerProxy
diff --git a/car-support-lib/src/android/support/car/hardware/CarSensorsProxy.java b/car-support-lib/src/android/support/car/hardware/CarSensorsProxy.java
index a0eb482..e110d07 100644
--- a/car-support-lib/src/android/support/car/hardware/CarSensorsProxy.java
+++ b/car-support-lib/src/android/support/car/hardware/CarSensorsProxy.java
@@ -322,7 +322,7 @@
case CarSensorManager.SENSOR_TYPE_COMPASS:
if (mLastMagneticFieldDataTime != 0 && mLastAccelerometerDataTime != 0) {
event = new CarSensorEvent(sensorType, Math.max(mLastMagneticFieldDataTime,
- mLastAccelerometerDataTime), 3, 0);
+ mLastAccelerometerDataTime), 3, 0, 0);
SensorManager.getRotationMatrix(mR, mI, mLastAccelerometerData,
mLastMagneticFieldData);
SensorManager.getOrientation(mR, mOrientation);
@@ -336,13 +336,13 @@
break;
case CarSensorManager.SENSOR_TYPE_LOCATION:
if (mLastLocationTime != 0) {
- event = new CarSensorEvent(sensorType, mLastLocationTime, 6, 3);
+ event = new CarSensorEvent(sensorType, mLastLocationTime, 6, 3, 0);
populateLocationCarSensorEvent(event, mLastLocation);
}
break;
case CarSensorManager.SENSOR_TYPE_ACCELEROMETER:
if (mLastAccelerometerDataTime != 0) {
- event = new CarSensorEvent(sensorType, mLastAccelerometerDataTime, 3, 0);
+ event = new CarSensorEvent(sensorType, mLastAccelerometerDataTime, 3, 0, 0);
event.floatValues[CarSensorEvent.INDEX_ACCELEROMETER_X] =
mLastAccelerometerData[0];
event.floatValues[CarSensorEvent.INDEX_ACCELEROMETER_Y] =
@@ -358,7 +358,7 @@
break;
case CarSensorManager.SENSOR_TYPE_GYROSCOPE:
if (mLastGyroscopeDataTime != 0) {
- event = new CarSensorEvent(sensorType, mLastGyroscopeDataTime, 3, 0);
+ event = new CarSensorEvent(sensorType, mLastGyroscopeDataTime, 3, 0, 0);
event.floatValues[CarSensorEvent.INDEX_GYROSCOPE_X] = mLastGyroscopeData[0];
event.floatValues[CarSensorEvent.INDEX_GYROSCOPE_Y] = mLastGyroscopeData[1];
event.floatValues[CarSensorEvent.INDEX_GYROSCOPE_Z] = mLastGyroscopeData[2];
@@ -430,7 +430,7 @@
int intValuesSize = CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_INT_INTERVAL * numberInView
+ CarSensorEvent.INDEX_GPS_SATELLITE_ARRAY_INT_OFFSET;
event = new CarSensorEvent(CarSensorManager.SENSOR_TYPE_GPS_SATELLITE, mLastGpsStatusTime,
- floatValuesSize, intValuesSize);
+ floatValuesSize, intValuesSize, 0);
event.intValues[CarSensorEvent.INDEX_GPS_SATELLITE_NUMBER_IN_USE] = numberInUse;
event.intValues[CarSensorEvent.INDEX_GPS_SATELLITE_NUMBER_IN_VIEW] = numberInView;
int i = 0;
diff --git a/car-support-lib/src/android/support/car/navigation/CarNavigationInstrumentCluster.java b/car-support-lib/src/android/support/car/navigation/CarNavigationInstrumentCluster.java
index f814b74..9d2175c 100644
--- a/car-support-lib/src/android/support/car/navigation/CarNavigationInstrumentCluster.java
+++ b/car-support-lib/src/android/support/car/navigation/CarNavigationInstrumentCluster.java
@@ -15,14 +15,15 @@
*/
package android.support.car.navigation;
-import android.support.annotation.IntDef;
-import android.support.annotation.RestrictTo;
+import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
+import android.os.Bundle;
+import android.support.annotation.IntDef;
+import android.support.annotation.Nullable;
+import android.support.annotation.RestrictTo;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import static android.support.annotation.RestrictTo.Scope.GROUP_ID;
-
/**
* Holds options related to navigation for the car's instrument cluster.
*/
@@ -44,13 +45,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;
/** @hide */
@RestrictTo(GROUP_ID)
@@ -99,6 +102,12 @@
}
/**
+ * Contains extra information about instrument cluster.
+ * @hide
+ */
+ public Bundle getExtra() { return mExtra; }
+
+ /**
* @return If instrument cluster is image, number of bits of colour depth it supports (8, 16,
* or 32), 0 otherwise.
*/
@@ -133,11 +142,24 @@
int imageWidth,
int imageHeight,
int imageColorDepthBits) {
- this.mMinIntervalMillis = minIntervalMillis;
- this.mType = type;
- this.mImageWidth = imageWidth;
- this.mImageHeight = imageHeight;
- this.mImageColorDepthBits = imageColorDepthBits;
+ this(minIntervalMillis, type, imageWidth, imageHeight, imageColorDepthBits, null);
+ }
+
+ /** @hide */
+ @RestrictTo(GROUP_ID)
+ CarNavigationInstrumentCluster(
+ int minIntervalMillis,
+ @ClusterType int type,
+ int imageWidth,
+ int imageHeight,
+ int imageColorDepthBits,
+ @Nullable Bundle extra) {
+ mMinIntervalMillis = minIntervalMillis;
+ mType = type;
+ mImageWidth = imageWidth;
+ mImageHeight = imageHeight;
+ mImageColorDepthBits = imageColorDepthBits;
+ mExtra = extra == null ? new Bundle() : new Bundle(extra);
}
@@ -149,6 +171,7 @@
"type: " + mType + ", " +
"imageWidth: " + mImageWidth + ", " +
"imageHeight: " + mImageHeight + ", " +
- "imageColourDepthBits: " + mImageColorDepthBits + " }";
+ "imageColourDepthBits: " + mImageColorDepthBits + ", " +
+ "extra: " + mExtra + " }";
}
}
diff --git a/car-support-lib/src/android/support/car/navigation/CarNavigationStatusManager.java b/car-support-lib/src/android/support/car/navigation/CarNavigationStatusManager.java
index efaf7da..adf9208 100644
--- a/car-support-lib/src/android/support/car/navigation/CarNavigationStatusManager.java
+++ b/car-support-lib/src/android/support/car/navigation/CarNavigationStatusManager.java
@@ -16,6 +16,7 @@
package android.support.car.navigation;
import android.graphics.Bitmap;
+import android.os.Bundle;
import android.support.annotation.IntDef;
import android.support.car.CarManagerBase;
import android.support.car.CarNotConnectedException;
@@ -188,10 +189,28 @@
public @interface DistanceUnit {}
/**
+ * Event type that holds information about next maneuver.
+ * @hide
+ */
+ public static final int EVENT_TYPE_NEXT_MANEUVER_INFO = 1;
+ /**
+ * Event type that holds information regarding distance/time to the next maneuver.
+ * @hide
+ */
+ public static final int EVENT_TYPE_NEXT_MANEUVER_COUNTDOWN = 2;
+ /**
+ * All custom (vendor-specific) event types should be equal or greater than this constant.
+ * @hide
+ */
+ public static final int EVENT_TYPE_VENDOR_FIRST = 1024;
+
+ /**
* Inform the instrument cluster if navigation is active or not.
* @param status New instrument cluster navigation status, one of the STATUS_* constants in
* this class.
* @throws CarNotConnectedException if the connection to the car service has been lost.
+ *
+ * @deprecated Use {@link #sendEvent(int, Bundle)} instead.
*/
public abstract void sendNavigationStatus(@Status int status) throws CarNotConnectedException;
@@ -216,6 +235,8 @@
* @param turnSide Turn side ({@link #TURN_SIDE_LEFT}, {@link #TURN_SIDE_RIGHT} or {@link
* #TURN_SIDE_UNSPECIFIED}).
* @throws CarNotConnectedException if the connection to the car service has been lost.
+ *
+ * @deprecated Use {@link #sendEvent(int, Bundle)} instead.
*/
public abstract void sendNavigationTurnEvent(@TurnEvent int turnEvent, CharSequence eventName,
int turnAngle, int turnNumber, @TurnSide int turnSide) throws CarNotConnectedException;
@@ -231,6 +252,7 @@
* imageColorDepthBits) in the initial NavigationStatusService call.
*
* @hide only first party applications may send a custom image to the cluster.
+ * @deprecated Use {@link #sendEvent(int, Bundle)} instead.
*/
public abstract void sendNavigationTurnEvent(@TurnEvent int turnEvent, CharSequence eventName,
int turnAngle, int turnNumber, Bitmap image, @TurnSide int turnSide)
@@ -247,11 +269,31 @@
* file.
* @return Returns {@code true} if successful.
* @throws CarNotConnectedException if the connection to the car service has been lost.
+ *
+ * @deprecated Use {@link #sendEvent(int, Bundle)} instead.
*/
public abstract void sendNavigationTurnDistanceEvent(int distanceMeters, int timeSeconds,
int displayDistanceMillis, int displayDistanceUnit) throws CarNotConnectedException;
/**
+ * Sends events from navigation app to instrument cluster.
+ *
+ * @param eventType event type, the value could be either
+ * {@link #EVENT_TYPE_NEXT_MANEUVER_INFO}, {@link EVENT_TYPE_NEXT_MANEUVER_COUNTDOWN}, or
+ * vendor-specific code.
+ *
+ * @param bundle object that holds data about the event
+ * @throws android.car.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 abstract void sendEvent(int eventType, Bundle bundle)
+ throws CarNotConnectedException;
+
+ /**
* @param callback {@link CarNavigationCallback} to be registered, replacing any existing
* listeners.
* @throws CarNotConnectedException if the connection to the car service has been lost.
diff --git a/car-support-lib/src/android/support/car/navigation/CarNavigationStatusManagerEmbedded.java b/car-support-lib/src/android/support/car/navigation/CarNavigationStatusManagerEmbedded.java
index f96952b..a72e20e 100644
--- a/car-support-lib/src/android/support/car/navigation/CarNavigationStatusManagerEmbedded.java
+++ b/car-support-lib/src/android/support/car/navigation/CarNavigationStatusManagerEmbedded.java
@@ -16,6 +16,7 @@
package android.support.car.navigation;
import android.graphics.Bitmap;
+import android.os.Bundle;
import android.support.car.CarNotConnectedException;
/**
@@ -71,6 +72,15 @@
}
@Override
+ public void sendEvent(int eventType, Bundle bundle) throws CarNotConnectedException {
+ try {
+ mManager.sendEvent(eventType, bundle);
+ } catch (android.car.CarNotConnectedException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ @Override
public void onCarDisconnected() {
//nothing to do
}
@@ -103,6 +113,7 @@
return null;
}
return new CarNavigationInstrumentCluster(ic.getMinIntervalMillis(), ic.getType(),
- ic.getImageWidth(), ic.getImageHeight(), ic.getImageColorDepthBits());
+ ic.getImageWidth(), ic.getImageHeight(), ic.getImageColorDepthBits(),
+ ic.getExtra());
}
}
diff --git a/car-usb-handler/Android.mk b/car-usb-handler/Android.mk
index c050854..3194250 100644
--- a/car-usb-handler/Android.mk
+++ b/car-usb-handler/Android.mk
@@ -14,6 +14,8 @@
#
#
+ifneq ($(TARGET_BUILD_PDK), true)
+
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
@@ -33,3 +35,5 @@
LOCAL_JAVA_LIBRARIES += android.car
include $(BUILD_PACKAGE)
+
+endif
diff --git a/car_product/bootanimations/bootanimation-832.zip b/car_product/bootanimations/bootanimation-832.zip
new file mode 100644
index 0000000..76b9c82
--- /dev/null
+++ b/car_product/bootanimations/bootanimation-832.zip
Binary files differ
diff --git a/car_product/bootanimations/square_280/bootanimation.zip b/car_product/bootanimations/square_280/bootanimation.zip
deleted file mode 100644
index ab4d79f..0000000
--- a/car_product/bootanimations/square_280/bootanimation.zip
+++ /dev/null
Binary files differ
diff --git a/car_product/bootanimations/square_320/bootanimation.zip b/car_product/bootanimations/square_320/bootanimation.zip
deleted file mode 100644
index f621ad8..0000000
--- a/car_product/bootanimations/square_320/bootanimation.zip
+++ /dev/null
Binary files differ
diff --git a/car_product/bootanimations/square_360/bootanimation.zip b/car_product/bootanimations/square_360/bootanimation.zip
deleted file mode 100644
index 3cb32f4..0000000
--- a/car_product/bootanimations/square_360/bootanimation.zip
+++ /dev/null
Binary files differ
diff --git a/car_product/build/car.mk b/car_product/build/car.mk
index 1c2e7ee..9984726 100644
--- a/car_product/build/car.mk
+++ b/car_product/build/car.mk
@@ -33,7 +33,9 @@
PRODUCT_PACKAGES += \
EmbeddedKitchenSinkApp \
VmsPublisherClientSample \
- VmsSubscriberClientSample
+ VmsSubscriberClientSample \
+ android.car.cluster.loggingrenderer \
+ DirectRenderingClusterSample \
PRODUCT_COPY_FILES := \
frameworks/av/media/libeffects/data/audio_effects.conf:system/etc/audio_effects.conf \
@@ -82,7 +84,6 @@
LocalMediaPlayer \
CarMediaApp \
CarMessengerApp \
- Stream \
CarHvacApp \
CarMapsPlaceholder \
CarLatinIME \
@@ -92,7 +93,7 @@
# Boot animation
PRODUCT_COPY_FILES += \
- packages/services/Car/car_product/bootanimations/square_280/bootanimation.zip:system/media/bootanimation.zip
+ packages/services/Car/car_product/bootanimations/bootanimation-832.zip:system/media/bootanimation.zip
PRODUCT_PROPERTY_OVERRIDES += \
fmas.spkr_6ch=35,20,110 \
diff --git a/car_product/build/car_base.mk b/car_product/build/car_base.mk
index cbc461c..815b9ba 100644
--- a/car_product/build/car_base.mk
+++ b/car_product/build/car_base.mk
@@ -17,7 +17,7 @@
# Base platform for car builds
# car packages should be added to car.mk instead of here
-PRODUCT_PACKAGE_OVERLAYS := packages/services/Car/car_product/overlay
+PRODUCT_PACKAGE_OVERLAYS += packages/services/Car/car_product/overlay
PRODUCT_PACKAGES += \
ContactsProvider \
@@ -94,7 +94,9 @@
PRODUCT_COPY_FILES += \
frameworks/native/data/etc/android.hardware.type.automotive.xml:system/etc/permissions/android.hardware.type.automotive.xml
-PRODUCT_PACKAGES += android.hardware.automotive.vehicle@2.1-service
+# Default permission grant exceptions
+PRODUCT_COPY_FILES += \
+ packages/services/Car/car_product/build/default-car-permissions.xml:system/etc/default-permissions/default-car-permissions.xml
$(call inherit-product, $(SRC_TARGET_DIR)/product/core_minimal.mk)
diff --git a/car_product/build/default-car-permissions.xml b/car_product/build/default-car-permissions.xml
new file mode 100644
index 0000000..c36f78c
--- /dev/null
+++ b/car_product/build/default-car-permissions.xml
@@ -0,0 +1,54 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+ This file contains permissions to be granted by default. Default
+ permissions are granted to special platform components and to apps
+ that are approved to get default grants. The special components
+ are apps that are expected tto work out-of-the-box as they provide
+ core use cases such as default dialer, default email, etc. These
+ grants are managed by the platform. The apps that are additionally
+ approved for default grants are ones that provide carrier specific
+ functionality, ones legally required at some location, ones providing
+ alternative disclosure and opt-out UI, ones providing highlight features
+ of a dedicated device, etc. This file contains only the latter exceptions.
+ Fixed permissions cannot be controlled by the user and need a special
+ approval. Typically these are to ensure either legally mandated functions
+ or the app is considered a part of the OS.
+-->
+
+<exceptions>
+
+ <!-- This is an example of an exception:
+ <exception
+ package="foo.bar.permission"
+ <permission name="android.permission.READ_CONTACTS" fixed="true"/>
+ <permission name="android.permission.READ_CALENDAR" fixed="false"/>
+ </exception>
+ -->
+
+ <exception
+ package="com.android.car.messenger">
+ <!-- Contacts -->
+ <permission name="android.permission.READ_CONTACTS" fixed="false"/>
+
+ <!-- SMS -->
+ <permission name="android.permission.SEND_SMS" fixed="false"/>
+ <permission name="android.permission.READ_SMS" fixed="false"/>
+ </exception>
+
+</exceptions>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values-night/colors.xml b/car_product/overlay/frameworks/base/core/res/res/values-night/colors.xml
index 2774c9e..9865fe7 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values-night/colors.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values-night/colors.xml
@@ -19,4 +19,19 @@
<resources>
<!-- The background color for the container of notification actions. -->
<color name="notification_action_list">#ff11181d</color> <!-- Dark Blue Grey 800 -->
+
+ <!-- The primary text color if the text is on top of a dark background. -->
+ <color name="notification_primary_text_color_light">#fff5f5f5</color> <!-- Grey 100 -->
+
+ <!-- The primary text color if the text is on top of a light background. -->
+ <color name="notification_primary_text_color_dark">#ff212121</color> <!-- Grey 900 -->
+
+ <!-- The secondary text color if the text is on top of a dark background. -->
+ <color name="notification_secondary_text_color_light">#ffe0e0e0</color> <!-- Grey 300 -->
+
+ <!-- The background color of a notification card. -->
+ <color name="notification_material_background_color">#ff172026</color> <!-- Dark Blue Grey 700 -->
+
+ <!-- The secondary text color if the text is on top of a dark background. -->
+ <color name="notification_secondary_text_color_dark">#ff6B6B6B</color> <!-- Grey 650 -->
</resources>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values/colors.xml b/car_product/overlay/frameworks/base/core/res/res/values/colors.xml
index f7a7a12..1efd0eb 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values/colors.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values/colors.xml
@@ -19,4 +19,23 @@
<resources>
<!-- The background color for the container of notification actions. -->
<color name="notification_action_list">#ffeeeeee</color> <!-- Grey 200 -->
+
+ <!-- The primary text color if the text is on top of a light background. -->
+ <color name="notification_primary_text_color_light">#ff212121</color> <!-- Grey 900 -->
+
+ <!-- The primary text color if the text is on top of a dark background. -->
+ <color name="notification_primary_text_color_dark">#fff5f5f5</color> <!-- Grey 100 -->
+
+ <!-- The secondary text color if the text is on top of a dark background. -->
+ <color name="notification_secondary_text_color_light">#ff6B6B6B</color> <!-- Grey 650 -->
+
+ <!-- The secondary text color if the text is on top of a dark background. -->
+ <color name="notification_secondary_text_color_dark">#ffe0e0e0</color> <!-- Grey 300 -->
+
+ <!-- The background color of a notification card. -->
+ <color name="notification_material_background_color">#fffafafa</color> <!-- Grey 50 -->
+
+ <!-- The default color for text in a notification. This color is also the default color for
+ icons. -->
+ <color name="notification_default_color">@color/notification_primary_text_color_light</color>
</resources>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values/config.xml b/car_product/overlay/frameworks/base/core/res/res/values/config.xml
index a9fb0e7..ca8a8d8 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values/config.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values/config.xml
@@ -60,4 +60,13 @@
notification. If false, then the expand icon has to be clicked in order for the expand
to occur. -->
<bool name="config_notificationHeaderClickableForExpand">true</bool>
+
+ <!-- Night mode should be enabled. -->
+ <bool name="config_enableNightMode">true</bool>
+
+ <!-- The action buttons should always take the default color. -->
+ <bool name="config_tintNotificationActionButtons">false</bool>
+
+ <!-- Home screen(Launcher) app presence -->
+ <bool name="config_noHomeScreen">true</bool>
</resources>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values/dimens.xml b/car_product/overlay/frameworks/base/core/res/res/values/dimens.xml
index 125e1fb..314d757 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values/dimens.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values/dimens.xml
@@ -18,7 +18,9 @@
-->
<resources>
<dimen name="status_bar_height">56dp</dimen>
+ <!-- Both of these are used in separate positions so make sure that they remain the same. -->
<dimen name="navigation_bar_height_car_mode">112dp</dimen>
+ <dimen name="navigation_bar_height_landscape">112dp</dimen>
<dimen name="status_bar_icon_size">40dp</dimen>
<!-- The height of the header of a notification. -->
@@ -27,7 +29,7 @@
<!-- The absolute size of the notification expand icon. -->
<dimen name="notification_header_expand_icon_size">55dp</dimen>
- <!-- The top padding for the notification expand button. -->
+ <!-- The top padding for the notification expand button. -->
<dimen name="notification_expand_button_padding_top">0dp</dimen>
<!-- The end margin after the application icon in the notification header -->
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/drawable/ic_done_wht.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/drawable/ic_done_wht.xml
new file mode 100644
index 0000000..9e265d4
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/drawable/ic_done_wht.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2017, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License")
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="64dp"
+ android:height="64dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48">
+
+ <path
+ android:pathData="M0 0h48v48H0z" />
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M18 32.34L9.66 24l-2.83 2.83L18 38l24-24-2.83-2.83z" />
+</vector>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
new file mode 100644
index 0000000..08fe6d0
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2017, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License")
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.keyguard.KeyguardPINView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/keyguard_pin_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginLeft="@dimen/num_pad_margin_left"
+ android:layout_marginRight="@dimen/num_pad_margin_right">
+
+
+ <GridLayout
+ android:id="@+id/container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|left"
+ android:columnCount="3">
+
+ <!-- Row 1 -->
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key1"
+ style="@style/NumPadKeyButton"
+ app:digit="@string/one" />
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key2"
+ style="@style/NumPadKeyButton"
+ app:digit="@string/two" />
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key3"
+ style="@style/NumPadKeyButton"
+ app:digit="@string/three" />
+
+ <!-- Row 2 -->
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key4"
+ style="@style/NumPadKeyButton"
+ app:digit="@string/four" />
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key5"
+ style="@style/NumPadKeyButton"
+ app:digit="@string/five" />
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key6"
+ style="@style/NumPadKeyButton"
+ app:digit="@string/six" />
+
+ <!-- Row 3 -->
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key7"
+ style="@style/NumPadKeyButton"
+ app:digit="@string/seven" />
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key8"
+ style="@style/NumPadKeyButton"
+ app:digit="@string/eight" />
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key9"
+ style="@style/NumPadKeyButton"
+ app:digit="@string/nine" />
+
+ <!-- Row 4 -->
+ <Space
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ <com.android.keyguard.NumPadKey
+ android:id="@+id/key0"
+ style="@style/NumPadKeyButton"
+ app:digit="@string/zero" />
+ <ImageButton
+ android:id="@+id/delete_button"
+ style="@style/NumPadKeyButton"
+ android:gravity="center_vertical"
+ android:src="@drawable/ic_backspace_black_24dp"
+ android:clickable="true"
+ android:background="@drawable/ripple_drawable"
+ android:contentDescription="@string/keyboardview_keycode_delete"
+ android:layout_alignParentRight="true" />
+ </GridLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|right"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <com.android.keyguard.PasswordTextView
+ android:id="@+id/pinEntry"
+ android:layout_width="@dimen/keyguard_security_width"
+ android:layout_height="@dimen/pin_entry_height"
+ android:gravity="center"
+ app:scaledTextSize="@integer/password_text_view_scale"
+ android:contentDescription="@string/keyguard_accessibility_pin_area" />
+
+ <View
+ android:id="@+id/divider"
+ android:layout_width="@dimen/keyguard_security_width"
+ android:layout_height="@dimen/divider_height"
+ android:background="@android:color/white" />
+
+ <com.android.keyguard.AlphaOptimizedImageButton
+ android:id="@+id/key_enter"
+ android:layout_width="@dimen/num_pad_key_width"
+ android:layout_height="@dimen/num_pad_key_height"
+ android:src="@drawable/ic_done_wht"
+ android:background="@drawable/ripple_drawable"
+ android:layout_marginTop="@dimen/key_enter_margin_top"
+ android:contentDescription="@string/keyboardview_keycode_enter" />
+
+ <include layout="@layout/keyguard_message_area"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+ </FrameLayout>
+
+ <!-- KeyguardPinView references these resources ids in code so removing them will cause the
+ keyguard to crash. Instead put them down here where they are out of the way and set their
+ visibility to gone. -->
+ <com.android.keyguard.AlphaOptimizedRelativeLayout
+ android:id="@+id/row0"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/row1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/row2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/row3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+ <LinearLayout
+ android:id="@+id/row4"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+
+ <include layout="@layout/keyguard_eca"
+ android:id="@+id/keyguard_selector_fade_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_gravity="bottom|center_horizontal"
+ android:gravity="center_horizontal"
+ android:visibility="gone" />
+</com.android.keyguard.KeyguardPINView>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/dimens.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/dimens.xml
new file mode 100644
index 0000000..f1dbe75
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2017, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License")
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <dimen name="num_pad_margin_left">112dp</dimen>
+ <dimen name="num_pad_margin_right">144dp</dimen>
+ <dimen name="num_pad_key_width">160dp</dimen>
+ <dimen name="num_pad_key_height">128dp</dimen>
+ <dimen name="num_pad_key_margin">32dp</dimen>
+ <dimen name="num_pad_key_digit_size">48sp</dimen>
+ <dimen name="num_pad_key_text_size">24sp</dimen>
+ <dimen name="pin_entry_height">@dimen/num_pad_key_height</dimen>
+ <dimen name="divider_height">1dp</dimen>
+ <dimen name="key_enter_margin_top">128dp</dimen>
+</resources>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/integers.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/integers.xml
new file mode 100644
index 0000000..0408ca4
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/integers.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2017, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License")
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <integer name="password_text_view_scale">72</integer>
+</resources>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/strings.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/strings.xml
new file mode 100644
index 0000000..cabeec2
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2017, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License")
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="zero">0</string>
+ <string name="one">1</string>
+ <string name="two">2</string>
+ <string name="three">3</string>
+ <string name="four">4</string>
+ <string name="five">5</string>
+ <string name="six">6</string>
+ <string name="seven">7</string>
+ <string name="eight">8</string>
+ <string name="nine">9</string>
+</resources>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/styles.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/styles.xml
new file mode 100644
index 0000000..c1e9622
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res-keyguard/values/styles.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2017, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License")
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <!-- The style for the volume icons in the volume dialog. This style makes the icon scale to
+ fit its container since auto wants the icon to be larger. The padding is added to make it
+ so the icon does not press along the edges of the dialog. -->
+ <style name="NumPadKeyButton">
+ <item name="android:layout_width">@dimen/num_pad_key_width</item>
+ <item name="android:layout_height">@dimen/num_pad_key_height</item>
+ <item name="android:layout_margin">@dimen/num_pad_key_margin</item>
+ <item name="textView">@id/pinEntry</item>
+ </style>
+
+ <style name="Widget.TextView.NumPadKey" parent="@android:style/Widget.TextView">
+ <!-- Only replaces the text size. -->
+ <item name="android:textSize">@dimen/num_pad_key_digit_size</item>
+ </style>
+
+ <style name="Widget.TextView.NumPadKey.Klondike" parent="Widget.TextView.NumPadKey">
+ <!-- Only replaces the text size. -->
+ <item name="android:textSize">@dimen/num_pad_key_text_size</item>
+ </style>
+</resources>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/car_ic_seekbar_thumb.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/car_ic_seekbar_thumb.xml
new file mode 100644
index 0000000..a29ddc8
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/car_ic_seekbar_thumb.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <solid android:color="@color/car_teal_700" />
+ <size
+ android:width="30dp"
+ android:height="30dp" />
+ <stroke
+ android:width="1dp"
+ android:color="@color/car_teal_700" />
+</shape>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/car_ic_seekbar_track.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/car_ic_seekbar_track.xml
new file mode 100644
index 0000000..2b42d9d
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/car_ic_seekbar_track.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2017 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Use android provided id, as seekbar is expecting that -->
+ <item android:id="@android:id/background">
+ <shape android:shape="line">
+ <corners
+ android:radius="3dp"/>
+ <stroke
+ android:width="3dp"
+ android:color="@color/car_grey_300" />
+ </shape>
+ </item>
+ <item android:id="@android:id/progress">
+ <clip>
+ <shape android:shape="line">
+ <stroke
+ android:width="6dp"
+ android:color="@color/car_teal_700" />
+ </shape>
+ </clip>
+ </item>
+</layer-list>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/notification_material_bg.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/notification_material_bg.xml
new file mode 100644
index 0000000..039833b
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/notification_material_bg.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/notification_ripple_untinted_color">
+ <item>
+ <shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/notification_material_background_color" />
+ <corners
+ android:radius="@dimen/notification_shadow_radius"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/notification_material_bg_dim.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/notification_material_bg_dim.xml
new file mode 100644
index 0000000..90c793f
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/notification_material_bg_dim.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/notification_ripple_untinted_color">
+ <item>
+ <shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/notification_material_background_color" />
+ <corners
+ android:radius="@dimen/notification_shadow_radius"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/remote_input_bg.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/remote_input_bg.xml
new file mode 100644
index 0000000..3120679
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/remote_input_bg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#ff6c6c6c" />
+ <corners
+ android:bottomRightRadius="16dp"
+ android:bottomLeftRadius="16dp"/>
+</shape>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/volume_dialog_background.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/volume_dialog_background.xml
new file mode 100644
index 0000000..345692f
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/drawable/volume_dialog_background.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" >
+ <solid android:color="?android:attr/colorBackgroundFloating" />
+ <padding
+ android:bottom="5dp"
+ android:left="5dp"
+ android:right="5dp"
+ android:top="5dp" />
+ <corners android:bottomLeftRadius="20dp"
+ android:bottomRightRadius="20dp"/>
+</shape>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/layout/volume_dialog_row.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/layout/volume_dialog_row.xml
new file mode 100644
index 0000000..069936c
--- /dev/null
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/volume_row_height"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/volume_row_padding_bottom" >
+
+ <!-- don't need this view in car, the visibility is overriden by system UI, so set the
+ layout_height to 0dp.
+ TODO: figure out a better way to switch UI based on FEATURE_AUTOMOTIVE -->
+ <TextView
+ android:id="@+id/volume_row_header"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:maxLines="1"
+ android:textSize="40sp"
+ android:textColor="?android:attr/textColorSecondary"
+ android:paddingStart="@dimen/volume_row_header_padding_start" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/line_item_height">
+ <com.android.keyguard.AlphaOptimizedImageButton
+ android:id="@+id/volume_row_icon"
+ style="@style/VolumeButtons"
+ android:tint="@color/car_grey_900"
+ android:gravity="center_vertical"
+ android:layout_gravity="center_vertical"
+ android:layout_width="@dimen/volume_icon_size"
+ android:layout_height="@dimen/volume_icon_size"
+ android:layout_marginStart="@dimen/car_keyline_1"
+ android:soundEffectsEnabled="false" />
+
+ <SeekBar
+ android:id="@+id/volume_row_slider"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="@dimen/car_keyline_3"
+ android:progressDrawable="@drawable/car_ic_seekbar_track"
+ android:thumb="@drawable/car_ic_seekbar_thumb"
+ android:focusable="true"
+ android:focusableInTouchMode="true"/>
+ </FrameLayout>
+</LinearLayout>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/values-night/colors.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/values-night/colors.xml
index be66b5b..26479a5 100644
--- a/car_product/overlay/frameworks/base/packages/SystemUI/res/values-night/colors.xml
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/values-night/colors.xml
@@ -19,4 +19,7 @@
<resources>
<color name="status_bar_background_color">#ff000000</color>
<color name="system_bar_background_opaque">#ff0c1013</color>
+
+ <!-- The color of the ripples on the untinted notifications -->
+ <color name="notification_ripple_untinted_color">@color/ripple_material_dark</color>
</resources>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/arrays_car.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/arrays_car.xml
index 9208135..94a6d45 100644
--- a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/arrays_car.xml
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/arrays_car.xml
@@ -33,7 +33,7 @@
<!-- Launch the lenspicker for all the facets. The lens picker will trampoline into the last run app or display a list of valid apps -->
<item>intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x14000000;package=com.android.support.car.lenspicker;end</item>
<item>intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x14000000;package=com.android.support.car.lenspicker;end</item>
- <item>intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x14000000;package=com.android.support.car.lenspicker;S.system_command=show_notifications;end</item>
+ <item>intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x14000000;package=com.android.support.car.lenspicker;S.system_command=toggle_notifications;end</item>
<item>intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x14000000;package=com.android.support.car.lenspicker;end</item>
<item>intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x14000000;package=com.android.support.car.lenspicker;end</item>
</array>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/colors.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/colors.xml
index b66ff92..54d926f 100644
--- a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/colors.xml
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/colors.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
-** Copyright 2016, The Android Open Source Project
+** Copyright 2017, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -31,4 +31,11 @@
<!-- The color of the dividing line between grouped notifications. -->
<color name="notification_divider_color">@*android:color/notification_action_list</color>
+
+ <!-- The color of the ripples on the untinted notifications -->
+ <color name="notification_ripple_untinted_color">@color/ripple_material_light</color>
+
+ <color name="car_teal_700">#ff00796b</color>
+ <color name="car_grey_300">#ffe0e0e0</color>
+ <color name="car_grey_900">#ff212121</color>
</resources>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/config.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/config.xml
index a9f231c..e48a06a 100644
--- a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/config.xml
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/config.xml
@@ -30,22 +30,6 @@
<!-- No need to draw a background around a notification because there is no gear icon. -->
<bool name="config_drawNotificationBackground">false</bool>
- <!-- No quick settings the quick settings row should not be shown.-->
- <bool name="config_showQuickSettingsRow">false</bool>
-
- <!-- The quick settings are not available on the car and should not be editable. -->
- <bool name="config_showQuickSettingsEditingIcon">false</bool>
-
- <!-- The multi-user switcher should always be visible because quick settings cannot be
- expanded. Thus, there is no other way to access this. -->
- <bool name="config_alwaysShowMultiUserSwitcher">true</bool>
-
- <!-- The quick settings should not be available for expansion in the car. -->
- <bool name="config_showQuickSettingsExpandIndicator">false</bool>
-
- <!-- There are no quick settings, so it should not be revealed with scrolling. -->
- <bool name="config_enableQuickSettingsOverscrollExpansion">false</bool>
-
<!-- The notification shade should only be shown on a facet click and not by dragging. -->
<bool name="config_enableNotificationShadeDrag">false</bool>
diff --git a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/dimens.xml b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/dimens.xml
index 5d675fe..047a6b7 100644
--- a/car_product/overlay/frameworks/base/packages/SystemUI/res/values/dimens.xml
+++ b/car_product/overlay/frameworks/base/packages/SystemUI/res/values/dimens.xml
@@ -82,19 +82,19 @@
<dimen name="qs_panel_width">-1px</dimen>
<!-- Height of a small notification in the status bar-->
- <dimen name="notification_min_height">600dp</dimen>
+ <dimen name="notification_min_height">192dp</dimen>
<!-- Height of a small notification in the status bar which was used before android N -->
- <dimen name="notification_min_height_legacy">600dp</dimen>
+ <dimen name="notification_min_height_legacy">192dp</dimen>
<!-- Height of a large notification in the status bar -->
- <dimen name="notification_max_height">600dp</dimen>
+ <dimen name="notification_max_height">400dp</dimen>
<!-- Height of a heads up notification in the status bar for legacy custom views -->
- <dimen name="notification_max_heads_up_height_legacy">600dp</dimen>
+ <dimen name="notification_max_heads_up_height_legacy">400dp</dimen>
<!-- Height of a heads up notification in the status bar -->
- <dimen name="notification_max_heads_up_height">600dp</dimen>
+ <dimen name="notification_max_heads_up_height">400dp</dimen>
<!-- Height of the status bar header bar -->
<dimen name="status_bar_header_height">54dp</dimen>
@@ -136,4 +136,23 @@
value is smaller than notification_children_container_header_height to bring the first
child closer so there is less wasted space. -->
<dimen name="notification_children_container_margin_top">68dp</dimen>
+
+ <!-- The height of the quick settings footer that holds the user switcher, settings icon,
+ etc. in the car setting.-->
+ <dimen name="qs_footer_height">74dp</dimen>
+
+ <dimen name="volume_dialog_side_margin">@dimen/side_margin</dimen>
+
+ <dimen name="volume_dialog_elevation">6dp</dimen>
+
+ <dimen name="volume_dialog_row_margin_end">@dimen/car_keyline_3</dimen>
+
+ <dimen name="volume_dialog_row_padding_end">0dp</dimen>
+
+ <dimen name="line_item_height">128dp</dimen>
+ <dimen name="volume_icon_size">96dp</dimen>
+ <dimen name="side_margin">148dp</dimen>
+ <dimen name="car_keyline_1">24dp</dimen>
+ <dimen name="car_keyline_2">96dp</dimen>
+ <dimen name="car_keyline_3">128dp</dimen>
</resources>
diff --git a/car_product/overlay/packages/services/Telecomm/res/values/config.xml b/car_product/overlay/packages/services/Telecomm/res/values/config.xml
index 33139ac..b19ded4 100644
--- a/car_product/overlay/packages/services/Telecomm/res/values/config.xml
+++ b/car_product/overlay/packages/services/Telecomm/res/values/config.xml
@@ -31,7 +31,7 @@
<string name="ui_default_package" translatable="false">com.android.car.dialer</string>
<!-- Class name for the default in-call UI Service [DO NOT TRANSLATE] -->
- <string name="incall_default_class" translatable="false">com.android.car.dialer.telecom.embedded.InCallServiceImpl</string>
+ <string name="incall_default_class" translatable="false">com.android.car.dialer.telecom.InCallServiceImpl</string>
<!-- Class name for the default main dialer activity [DO NOT TRANSLATE] -->
<string name="dialer_default_class" translatable="false">com.android.car.dialer.TelecomActivity</string>
diff --git a/car_product/sepolicy/evs_app.te b/car_product/sepolicy/evs_app.te
deleted file mode 100644
index 0e8881e..0000000
--- a/car_product/sepolicy/evs_app.te
+++ /dev/null
@@ -1,14 +0,0 @@
-# evs app
-type evs_app, domain;
-type evs_app_exec, exec_type, file_type;
-
-allow evs_app evs_app_exec:dir search;
-allow evs_app evs_driver:binder call;
-allow evs_app evs_mock:binder call;
-allow evs_app gpu_device:chr_file ioctl;
-allow evs_app hal_graphics_allocator_default:fd use;
-allow evs_app hal_vehicle_default:binder call;
-
-init_daemon_domain(evs_app)
-
-binder_use(evs_app);
diff --git a/car_product/sepolicy/evs_driver.te b/car_product/sepolicy/evs_driver.te
deleted file mode 100644
index 1307616..0000000
--- a/car_product/sepolicy/evs_driver.te
+++ /dev/null
@@ -1,12 +0,0 @@
-# evs_driver mock hardware driver service
-type evs_driver, domain;
-type evs_driver_exec, exec_type, file_type;
-
-allow evs_driver hwservicemanager:binder { call transfer };
-allow evs_driver hwservicemanager_prop:file { getattr open read };
-allow evs_driver device:dir { open read };
-allow evs_driver surfaceflinger:binder call;
-
-init_daemon_domain(evs_driver)
-
-binder_use(evs_driver);
diff --git a/car_product/sepolicy/evs_manager.te b/car_product/sepolicy/evs_manager.te
deleted file mode 100644
index f5c4ba8..0000000
--- a/car_product/sepolicy/evs_manager.te
+++ /dev/null
@@ -1,11 +0,0 @@
-# evs manager
-type evs_manager, domain;
-type evs_manager_exec, exec_type, file_type;
-
-allow evs_manager hwservicemanager:binder { call transfer };
-allow evs_manager hwservicemanager_prop:file { getattr open read };
-allow evs_manager evs_driver:binder call;
-
-init_daemon_domain(evs_manager)
-
-binder_use(evs_manager);
diff --git a/car_product/sepolicy/evs_mock.te b/car_product/sepolicy/evs_mock.te
deleted file mode 100644
index b89b1ba..0000000
--- a/car_product/sepolicy/evs_mock.te
+++ /dev/null
@@ -1,11 +0,0 @@
-# evs_mock mock hardware driver service
-type evs_mock, domain;
-type evs_mock_exec, exec_type, file_type;
-
-allow evs_mock hwservicemanager:binder { call transfer };
-allow evs_mock hwservicemanager_prop:file { getattr open read };
-allow evs_mock hal_graphics_allocator_default:fd use;
-
-init_daemon_domain(evs_mock)
-
-binder_use(evs_mock);
diff --git a/car_product/sepolicy/file_contexts b/car_product/sepolicy/file_contexts
index 53759c7..3705765 100644
--- a/car_product/sepolicy/file_contexts
+++ b/car_product/sepolicy/file_contexts
@@ -7,10 +7,4 @@
/(vendor|system/vendor)/bin/hw/android\.hardware\.automotive\.vehicle@2\.0-service u:object_r:hal_vehicle_default_exec:s0
/(vendor|system/vendor)/bin/hw/android\.hardware\.automotive\.vehicle@2\.1-service u:object_r:hal_vehicle_default_exec:s0
-/(vendor|system/vendor)/bin/hw/android\.hardware\.automotive\.evs@1\.0-service u:object_r:evs_mock_exec:s0
-/system/bin/android\.hardware\.automotive\.evs@1\.0-sample u:object_r:evs_driver_exec:s0
-/system/bin/android\.automotive\.evs\.manager@1\.0 u:object_r:evs_manager_exec:s0
-/system/bin/evs_app u:object_r:evs_app_exec:s0
-/system/etc/automotive/evs(/.*)? u:object_r:evs_app_exec:s0
-
###################################
diff --git a/car_product/sepolicy/priv_app.te b/car_product/sepolicy/priv_app.te
index de6077e..e6b5292 100644
--- a/car_product/sepolicy/priv_app.te
+++ b/car_product/sepolicy/priv_app.te
@@ -1,3 +1 @@
hal_client_domain(priv_app, hal_vehicle)
-
-get_prop(priv_app, opengles_prop)
diff --git a/car_product/sepolicy/property.te b/car_product/sepolicy/property.te
index 0f4e53f..64340e7 100644
--- a/car_product/sepolicy/property.te
+++ b/car_product/sepolicy/property.te
@@ -1,6 +1,3 @@
type hw_cabl_prop, property_type;
type wlan_driver_prop, property_type;
-
-type opengles_prop, property_type;
-
type car_prop, property_type;
diff --git a/car_product/sepolicy/property_contexts b/car_product/sepolicy/property_contexts
index 67b90f6..3b680cb 100644
--- a/car_product/sepolicy/property_contexts
+++ b/car_product/sepolicy/property_contexts
@@ -2,4 +2,3 @@
wlan.driver. u:object_r:wlan_driver_prop:s0
boot.car_service_created u:object_r:car_prop:s0
-ro.opengles. u:object_r:opengles_prop:s0
\ No newline at end of file
diff --git a/car_product/sepolicy/system_server.te b/car_product/sepolicy/system_server.te
index 4e0da64..b6785c1 100644
--- a/car_product/sepolicy/system_server.te
+++ b/car_product/sepolicy/system_server.te
@@ -1,6 +1,4 @@
# Set wlan.driver.* properties.
set_prop(system_server, wlan_driver_prop)
-get_prop(system_server opengles_prop)
-
dontaudit system_server self:capability sys_module;
diff --git a/evs/app/Android.mk b/evs/app/Android.mk
index f5d9b6e..2b15865 100644
--- a/evs/app/Android.mk
+++ b/evs/app/Android.mk
@@ -44,7 +44,8 @@
LOCAL_MODULE:= evs_app
LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES -DLOG_TAG=\"EVSAPP\"
+LOCAL_CFLAGS += -DLOG_TAG=\"EvsApp\"
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
include $(BUILD_EXECUTABLE)
diff --git a/evs/app/EvsStateControl.cpp b/evs/app/EvsStateControl.cpp
index 79533f7..1a925e6 100644
--- a/evs/app/EvsStateControl.cpp
+++ b/evs/app/EvsStateControl.cpp
@@ -203,8 +203,6 @@
bool EvsStateControl::selectStateForCurrentConditions() {
- ALOGV("selectStateForCurrentConditions");
-
static int32_t sDummyGear = int32_t(VehicleGear::GEAR_REVERSE);
static int32_t sDummySignal = int32_t(VehicleTurnSignal::NONE);
@@ -214,9 +212,10 @@
ALOGE("GEAR_SELECTION not available from vehicle. Exiting.");
return false;
}
- if (invokeGet(&mTurnSignalValue) != StatusCode::OK) {
+ if ((mTurnSignalValue.prop == 0) || (invokeGet(&mTurnSignalValue) != StatusCode::OK)) {
// Silently treat missing turn signal state as no turn signal active
mTurnSignalValue.value.int32Values.setToExternal(&sDummySignal, 1);
+ mTurnSignalValue.prop = 0;
}
} else {
// While testing without a vehicle, behave as if we're in reverse for the first 20 seconds
@@ -236,7 +235,7 @@
}
// Choose our desired EVS state based on the current car state
- // TODO: Update this logic, and include user input when choosing if a view should be presented
+ // TODO: Update this logic, and consider user input when choosing if a view should be presented
State desiredState = OFF;
if (mGearValue.value.int32Values[0] == int32_t(VehicleGear::GEAR_REVERSE)) {
desiredState = REVERSE;
@@ -248,16 +247,12 @@
desiredState = PARKING;
}
- ALOGD("Selected state %d.", desiredState);
-
// Apply the desire state
return configureEvsPipeline(desiredState);
}
StatusCode EvsStateControl::invokeGet(VehiclePropValue *pRequestedPropValue) {
- ALOGV("invokeGet");
-
StatusCode status = StatusCode::TRY_AGAIN;
// Call the Vehicle HAL, which will block until the callback is complete
@@ -265,7 +260,9 @@
[pRequestedPropValue, &status]
(StatusCode s, const VehiclePropValue& v) {
status = s;
- *pRequestedPropValue = v;
+ if (s == StatusCode::OK) {
+ *pRequestedPropValue = v;
+ }
}
);
@@ -274,13 +271,12 @@
bool EvsStateControl::configureEvsPipeline(State desiredState) {
- ALOGV("configureEvsPipeline");
-
if (mCurrentState == desiredState) {
// Nothing to do here...
return true;
}
+ ALOGD("Switching to state %d.", desiredState);
ALOGD(" Current state %d has %zu cameras", mCurrentState,
mCameraList[mCurrentState].size());
ALOGD(" Desired state %d has %zu cameras", desiredState,
diff --git a/evs/app/StreamHandler.cpp b/evs/app/StreamHandler.cpp
index 5477642..28da6df 100644
--- a/evs/app/StreamHandler.cpp
+++ b/evs/app/StreamHandler.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "EVSAPP"
-
#include "StreamHandler.h"
#include <stdio.h>
diff --git a/evs/app/config.json.readme b/evs/app/config.json.readme
new file mode 100644
index 0000000..982e3c9
--- /dev/null
+++ b/evs/app/config.json.readme
@@ -0,0 +1,42 @@
+// With comments included, this file is no longer legal JSON, but serves to illustrate
+// the format of the configuration file the evs_app expects to read at startup to configure itself
+// for a specific car.
+// In addition to the configuration file, an image to be used to represent the car is expected
+// to be provided in CarFromTop.png.
+// Throughout this file, units of length are arbitrary, but must all be the same units.
+// X is right, Y is forward, Z is up (right handed coordinate system).
+// The origin is at the center of the read axel at ground level.
+// Units for angles are in degrees.
+// Yaw is measured from the front of the car, positive to the left (postive Z rotation).
+// Pitch is measured from the horizon, positive upward (postive X rotation).
+// Roll is always assumed to be zero.
+
+{
+ "car" : { // This section describes the geometry of the car
+ "width" : 76.7, // The width of the car body
+ "wheelBase" : 117.9, // The distance between the front and read axel
+ "frontExtent" : 44.7, // The extent of the car body ahead of the front axel
+ "rearExtent" : 40 // The extent of the car body behind the read axel
+ },
+ "display" : { // This configures the dimensions of the surround view display
+ "frontRange" : 100, // How far to render the view in front of the front bumper
+ "rearRange" : 100 // How far the view extends behind the rear bumper
+ },
+ "graphic" : { // This maps the car texture into the projected view space
+ "frontPixel" : 23, // The pixel row in CarFromTop.png at which the front bumper appears
+ "rearPixel" : 223 // The pixel row in CarFromTop.png at which the back bumper ends
+ },
+ "cameras" : [ // This describes the cameras potentially available on the car
+ {
+ "cameraId" : "/dev/video32", // Camera ID exposed by EVS HAL
+ "function" : "reverse,park", // set of modes to which this camera contributes
+ "x" : 0.0, // Optical center distance right of vehicle center
+ "y" : -40.0, // Optical center distance forward of rear axel
+ "z" : 48, // Optical center distance above ground
+ "yaw" : 180, // Optical axis degrees to the left of straight ahead
+ "pitch" : -30, // Optical axis degrees above the horizon
+ "hfov" : 125, // Horizontal field of view in degrees
+ "vfov" :103 // Vertical field of view in degrees
+ }
+ ]
+}
diff --git a/evs/app/evs_app.cpp b/evs/app/evs_app.cpp
index 5f8d64f..618e8cc 100644
--- a/evs/app/evs_app.cpp
+++ b/evs/app/evs_app.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "EVSAPP"
-
#include <stdio.h>
#include <hidl/HidlTransportSupport.h>
diff --git a/evs/manager/Android.mk b/evs/manager/Android.mk
index fe384d9..375a84c 100644
--- a/evs/manager/Android.mk
+++ b/evs/manager/Android.mk
@@ -28,6 +28,7 @@
LOCAL_MODULE_TAGS := optional
LOCAL_STRIP_MODULE := keep_symbols
+LOCAL_CFLAGS += -DLOG_TAG=\"EvsManager\"
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
diff --git a/evs/manager/Enumerator.cpp b/evs/manager/Enumerator.cpp
index 7bfe6f0..7e53304 100644
--- a/evs/manager/Enumerator.cpp
+++ b/evs/manager/Enumerator.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "EvsManager"
-
#include "Enumerator.h"
namespace android {
diff --git a/evs/manager/HalCamera.cpp b/evs/manager/HalCamera.cpp
index 9e33717..41cff24 100644
--- a/evs/manager/HalCamera.cpp
+++ b/evs/manager/HalCamera.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "EvsManager"
-
#include "HalCamera.h"
#include "VirtualCamera.h"
#include "Enumerator.h"
diff --git a/evs/manager/VirtualCamera.cpp b/evs/manager/VirtualCamera.cpp
index 652caf6..af90f2c 100644
--- a/evs/manager/VirtualCamera.cpp
+++ b/evs/manager/VirtualCamera.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "EvsManager"
-
#include "VirtualCamera.h"
#include "HalCamera.h"
#include "Enumerator.h"
diff --git a/evs/manager/service.cpp b/evs/manager/service.cpp
index fe1136b..f9e45eb 100644
--- a/evs/manager/service.cpp
+++ b/evs/manager/service.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "EvsManager"
-
#include <unistd.h>
#include <hidl/HidlTransportSupport.h>
diff --git a/evs/sampleDriver/Android.mk b/evs/sampleDriver/Android.mk
index 231ab6e..c4463a9 100644
--- a/evs/sampleDriver/Android.mk
+++ b/evs/sampleDriver/Android.mk
@@ -35,6 +35,7 @@
LOCAL_MODULE_TAGS := optional
LOCAL_STRIP_MODULE := keep_symbols
+LOCAL_CFLAGS += -DLOG_TAG=\"EvsSampleDriver\"
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
diff --git a/evs/sampleDriver/EvsEnumerator.cpp b/evs/sampleDriver/EvsEnumerator.cpp
index 25b8133..d31f672 100644
--- a/evs/sampleDriver/EvsEnumerator.cpp
+++ b/evs/sampleDriver/EvsEnumerator.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "android.hardware.automotive.evs@1.0-display"
-
#include "EvsEnumerator.h"
#include "EvsV4lCamera.h"
#include "EvsGlDisplay.h"
diff --git a/evs/sampleDriver/EvsGlDisplay.cpp b/evs/sampleDriver/EvsGlDisplay.cpp
index 0f62e64..26d4c1d 100644
--- a/evs/sampleDriver/EvsGlDisplay.cpp
+++ b/evs/sampleDriver/EvsGlDisplay.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "android.hardware.automotive.evs@1.0-display"
-
#include "EvsGlDisplay.h"
#include <ui/GraphicBufferAllocator.h>
diff --git a/evs/sampleDriver/EvsV4lCamera.cpp b/evs/sampleDriver/EvsV4lCamera.cpp
index 045d7ab..f811a1f 100644
--- a/evs/sampleDriver/EvsV4lCamera.cpp
+++ b/evs/sampleDriver/EvsV4lCamera.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "android.hardware.automotive.evs@1.0-display"
-
#include "EvsV4lCamera.h"
#include "EvsEnumerator.h"
#include "bufferCopy.h"
diff --git a/evs/sampleDriver/GlWrapper.cpp b/evs/sampleDriver/GlWrapper.cpp
index a49eb20..eb45bc9 100644
--- a/evs/sampleDriver/GlWrapper.cpp
+++ b/evs/sampleDriver/GlWrapper.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "android.hardware.automotive.evs@1.0-display"
-
#include "GlWrapper.h"
#include <stdio.h>
diff --git a/evs/sampleDriver/VideoCapture.cpp b/evs/sampleDriver/VideoCapture.cpp
index 2122a2c..d216b29 100644
--- a/evs/sampleDriver/VideoCapture.cpp
+++ b/evs/sampleDriver/VideoCapture.cpp
@@ -13,8 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#define LOG_TAG "android.hardware.automotive.evs@1.0-display"
-
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
diff --git a/evs/sampleDriver/android.hardware.automotive.evs@1.0-sample.rc b/evs/sampleDriver/android.hardware.automotive.evs@1.0-sample.rc
index 81fe33a..7b11cf5 100644
--- a/evs/sampleDriver/android.hardware.automotive.evs@1.0-sample.rc
+++ b/evs/sampleDriver/android.hardware.automotive.evs@1.0-sample.rc
@@ -2,5 +2,5 @@
class hal
priority -20
user graphics
- group automotive_evs
+ group automotive_evs camera
onrestart restart evs_manager
diff --git a/evs/sampleDriver/bufferCopy.cpp b/evs/sampleDriver/bufferCopy.cpp
index a585040..6c8c3ef 100644
--- a/evs/sampleDriver/bufferCopy.cpp
+++ b/evs/sampleDriver/bufferCopy.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "android.hardware.automotive.evs@1.0-display"
-
#include "bufferCopy.h"
diff --git a/evs/sampleDriver/service.cpp b/evs/sampleDriver/service.cpp
index d73f758..41be742 100644
--- a/evs/sampleDriver/service.cpp
+++ b/evs/sampleDriver/service.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "android.hardware.automotive.evs@1.0-display"
-
#include <unistd.h>
#include <hidl/HidlTransportSupport.h>
diff --git a/evs/sepolicy/attributes b/evs/sepolicy/attributes
new file mode 100644
index 0000000..d31005b
--- /dev/null
+++ b/evs/sepolicy/attributes
@@ -0,0 +1,4 @@
+# EVS specific attributes
+attribute hal_evs;
+attribute hal_evs_client;
+attribute hal_evs_server;
diff --git a/evs/sepolicy/evs_app.te b/evs/sepolicy/evs_app.te
new file mode 100644
index 0000000..28e71a9
--- /dev/null
+++ b/evs/sepolicy/evs_app.te
@@ -0,0 +1,22 @@
+# evs app
+type evs_app, domain;
+hal_client_domain(evs_app, hal_evs)
+hal_client_domain(evs_app, hal_vehicle)
+
+# allow init to launch processes in this context
+type evs_app_exec, exec_type, file_type;
+init_daemon_domain(evs_app)
+
+# gets access to its own files on disk
+type evs_app_files, file_type;
+allow evs_app evs_app_files:file { getattr open read };
+allow evs_app evs_app_files:dir search;
+
+# Allow use of gralloc buffers and EGL
+allow evs_app hal_graphics_allocator_default:fd use;
+allow evs_app gpu_device:chr_file ioctl;
+allow evs_app gpu_device:chr_file { getattr open read write };
+
+# Permit communication with the vehicle HAL
+# (Communcations with the rest of the EVS stack is allowed via hal_evs)
+binder_call(evs_app, hal_vehicle);
\ No newline at end of file
diff --git a/evs/sepolicy/evs_default.te b/evs/sepolicy/evs_default.te
new file mode 100644
index 0000000..c90e34b
--- /dev/null
+++ b/evs/sepolicy/evs_default.te
@@ -0,0 +1,7 @@
+# evs_mock mock hardware driver service
+type hal_evs_default, domain;
+hal_server_domain(hal_evs_default, hal_evs)
+
+# allow init to launch processes in this context
+type hal_evs_default_exec, exec_type, file_type;
+init_daemon_domain(hal_evs_default)
diff --git a/evs/sepolicy/evs_driver.te b/evs/sepolicy/evs_driver.te
new file mode 100644
index 0000000..2226dd6
--- /dev/null
+++ b/evs/sepolicy/evs_driver.te
@@ -0,0 +1,20 @@
+# evs_mock mock hardware driver service
+type hal_evs_driver, domain;
+hal_server_domain(hal_evs_driver, hal_evs)
+
+# allow init to launch processes in this context
+type hal_evs_driver_exec, exec_type, file_type;
+init_daemon_domain(hal_evs_driver)
+
+# Allow use of USB devices, gralloc buffers, and surface flinger
+allow hal_evs_driver device:dir { open read };
+allow hal_evs_driver video_device:chr_file { ioctl open read write };
+hal_client_domain(hal_evs_driver, hal_graphics_allocator);
+
+allow hal_evs_driver gpu_device:chr_file { getattr ioctl open read write };
+binder_call(hal_evs_driver, surfaceflinger);
+allow hal_evs_driver surfaceflinger_service:service_manager find;
+allow hal_evs_driver hal_graphics_composer_default:fd use;
+allow hal_evs_driver hal_graphics_allocator_default_tmpfs:file { read write };
+allow hal_evs_driver self:capability dac_override;
+allow hal_evs_driver servicemanager:binder call;
diff --git a/evs/sepolicy/evs_manager.te b/evs/sepolicy/evs_manager.te
new file mode 100644
index 0000000..70a2cda
--- /dev/null
+++ b/evs/sepolicy/evs_manager.te
@@ -0,0 +1,8 @@
+# evs manager
+type evs_manager, domain;
+hal_server_domain(evs_manager, hal_evs)
+hal_client_domain(evs_manager, hal_evs)
+
+# allow init to launch processes in this context
+type evs_manager_exec, exec_type, file_type;
+init_daemon_domain(evs_manager)
diff --git a/evs/sepolicy/file_contexts b/evs/sepolicy/file_contexts
new file mode 100644
index 0000000..097a588
--- /dev/null
+++ b/evs/sepolicy/file_contexts
@@ -0,0 +1,12 @@
+
+###################################
+# Binaries associated with the default EVS stack, plus
+# the directory which contains the configuration for the evs_app
+#
+/(vendor|system/vendor)/bin/hw/android\.hardware\.automotive\.evs@1\.0-service u:object_r:hal_evs_default_exec:s0
+/system/bin/android\.hardware\.automotive\.evs@1\.0-sample u:object_r:hal_evs_driver_exec:s0
+/system/bin/android\.automotive\.evs\.manager@1\.0 u:object_r:evs_manager_exec:s0
+/system/bin/evs_app u:object_r:evs_app_exec:s0
+/system/etc/automotive/evs(/.*)? u:object_r:evs_app_files:s0
+
+###################################
diff --git a/evs/sepolicy/hal_evs.te b/evs/sepolicy/hal_evs.te
new file mode 100644
index 0000000..525f2be
--- /dev/null
+++ b/evs/sepolicy/hal_evs.te
@@ -0,0 +1,6 @@
+hwbinder_use(hal_evs_client)
+hwbinder_use(hal_evs_server)
+binder_call(hal_evs_client, hal_evs_server)
+binder_call(hal_evs_server, hal_evs_client)
+
+allow hal_evs_server hal_graphics_allocator_default:fd use;
\ No newline at end of file
diff --git a/service/Android.mk b/service/Android.mk
index 9bd5e12..2b926df 100644
--- a/service/Android.mk
+++ b/service/Android.mk
@@ -40,7 +40,6 @@
LOCAL_STATIC_JAVA_LIBRARIES += \
android.hidl.base-V1.0-java \
android.hardware.automotive.vehicle-V2.0-java \
- android.hardware.automotive.vehicle-V2.1-java \
vehicle-hal-support-lib \
car-systemtest \
@@ -60,7 +59,6 @@
LOCAL_STATIC_JAVA_LIBRARIES += \
android.hidl.base-V1.0-java \
android.hardware.automotive.vehicle-V2.0-java \
- android.hardware.automotive.vehicle-V2.1-java \
vehicle-hal-support-lib \
car-systemtest \
diff --git a/service/AndroidManifest.xml b/service/AndroidManifest.xml
index 175a8bb..e39fb9e 100644
--- a/service/AndroidManifest.xml
+++ b/service/AndroidManifest.xml
@@ -61,6 +61,12 @@
android:label="@string/car_permission_label_speed"
android:description="@string/car_permission_desc_speed" />
<permission
+ android:name="android.car.permission.VEHICLE_DYNAMICS_STATE"
+ android:permissionGroup="android.permission-group.CAR_INFORMATION"
+ android:protectionLevel="dangerous"
+ android:label="@string/car_permission_label_vehicle_dynamics_state"
+ android:description="@string/car_permission_desc_vehicle_dynamics_state" />
+ <permission
android:name="android.car.permission.CAR_VENDOR_EXTENSION"
android:protectionLevel="system|signature"
android:label="@string/car_permission_label_vendor_extension"
@@ -86,7 +92,7 @@
android:label="@string/car_permission_car_navigation_manager"
android:description="@string/car_permission_desc_car_navigation_manager" />
<permission
- android:name="android.car.permission.DIAGNOSTIC_READ"
+ android:name="android.car.permission.DIAGNOSTIC_READ_ALL"
android:protectionLevel="system|signature"
android:label="@string/car_permission_label_diag_read"
android:description="@string/car_permission_desc_diag_read" />
@@ -137,6 +143,17 @@
android:label="@string/car_permission_label_bind_input_service"
android:description="@string/car_permission_desc_bind_input_service"/>
+ <permission
+ android:name="android.car.permission.CAR_DISPLAY_IN_CLUSTER"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_car_display_in_cluster"
+ android:description="@string/car_permission_desc_car_display_in_cluster" />
+
+ <permission android:name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_car_cluster_control"
+ android:description="@string/car_permission_desc_car_cluster_control" />
+
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
<uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS" />
diff --git a/service/res/values/config.xml b/service/res/values/config.xml
index fd4a841..61cef59 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -58,7 +58,7 @@
<string name="inputService">android.car.input.service/.DefaultInputService</string>
- <string name="instrumentClusterRendererService">android.car.cluster.loggingrenderer/.LoggingClusterRenderingService</string>
+ <string name="instrumentClusterRendererService">android.car.cluster.sample/.SampleClusterServiceImpl</string>
<!-- Whether to enable Avtivity blocking for safety. When Activity blocking is enabled,
only whitelisted safe Activities will be allowed while car is not parked. -->
diff --git a/service/res/values/strings.xml b/service/res/values/strings.xml
index 1cd53ca..6cfff9a 100644
--- a/service/res/values/strings.xml
+++ b/service/res/values/strings.xml
@@ -44,6 +44,10 @@
<string name="car_permission_label_speed">Car speed</string>
<!-- Permission text: can access your car's speed [CHAR LIMIT=NONE] -->
<string name="car_permission_desc_speed">Access your car\'s speed.</string>
+ <!-- Permission text: can access your car's dynamics state [CHAR LIMIT=NONE] -->
+ <string name="car_permission_label_vehicle_dynamics_state">Vehicle dynamics state</string>
+ <!-- Permission text: can access your car's dynamic state [CHAR LIMIT=NONE] -->
+ <string name="car_permission_desc_vehicle_dynamics_state">Access your car\'s dynamics state</string>
<!-- Permission text: apps can access car-manufacturer specific data [CHAR LIMIT=NONE] -->
<string name="car_permission_label_vendor_extension">Car vendor channel</string>
<!-- Permission text: apps can access car-manufacturer specific data [CHAR LIMIT=NONE] -->
@@ -77,6 +81,11 @@
cluster [CHAR LIMIT=NONE] -->
<string name="car_permission_desc_car_navigation_manager">Report navigation data to instrument
cluster</string>
+ <string name="car_permission_car_display_in_cluster">Direct rendering to instrument cluster</string>
+ <string name="car_permission_desc_car_display_in_cluster">Allow an application to declare
+ activities to be displayed in the instrument cluster</string>
+ <string name="car_permission_car_cluster_control">Instrument cluster control</string>
+ <string name="car_permission_desc_car_cluster_control">Launch apps in the instrument cluster</string>
<string name="car_permission_label_bind_instrument_cluster_rendering">Instrument Cluster Rendering</string>
<string name="car_permission_desc_bind_instrument_cluster_rendering">Receive instrument cluster data</string>
diff --git a/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java b/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java
index e80b843..94cbdb1 100644
--- a/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java
+++ b/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java
@@ -16,6 +16,7 @@
package com.android.car;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.bluetooth.BluetoothA2dpSink;
import android.bluetooth.BluetoothAdapter;
@@ -39,6 +40,7 @@
import static android.car.settings.CarSettings.Secure.KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICES;
import static android.car.settings.CarSettings.Secure.KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICES;
+import android.car.CarBluetoothManager;
import android.os.ParcelUuid;
import android.os.Parcelable;
import android.os.UserHandle;
@@ -51,6 +53,8 @@
import android.os.RemoteException;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.lang.StringBuilder;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -123,36 +127,47 @@
// PerUserCarService related listeners
private final UserServiceConnectionCallback mServiceCallback;
+ // Car Bluetooth Priority Settings Manager
+ private final CarBluetoothService mCarBluetoothService;
+
// The Bluetooth profiles that the CarService will try to auto-connect on.
private final List<Integer> mProfilesToConnect;
+ private final List<Integer> mPrioritiesSupported;
private static final int MAX_CONNECT_RETRIES = 1;
-
private static final int PROFILE_NOT_AVAILABLE = -1;
// Device & Profile currently being connected on
private ConnectionParams mConnectionInFlight;
+ // Allow write to Settings.Secure
+ private boolean mAllowReadWriteToSettings = true;
public static BluetoothDeviceConnectionPolicy create(Context context,
CarCabinService carCabinService, CarSensorService carSensorService,
- PerUserCarServiceHelper userServiceHelper) {
+ PerUserCarServiceHelper userServiceHelper, CarBluetoothService bluetoothService) {
return new BluetoothDeviceConnectionPolicy(context, carCabinService, carSensorService,
- userServiceHelper);
+ userServiceHelper, bluetoothService);
}
private BluetoothDeviceConnectionPolicy(Context context, CarCabinService carCabinService,
- CarSensorService carSensorService, PerUserCarServiceHelper userServiceHelper) {
+ CarSensorService carSensorService, PerUserCarServiceHelper userServiceHelper,
+ CarBluetoothService bluetoothService) {
mContext = context;
mCarCabinService = carCabinService;
mCarSensorService = carSensorService;
mUserServiceHelper = userServiceHelper;
+ mCarBluetoothService = bluetoothService;
mCarUserServiceAccessLock = new ReentrantLock();
mProfilesToConnect = Arrays.asList(
BluetoothProfile.HEADSET_CLIENT, BluetoothProfile.A2DP_SINK,
BluetoothProfile.PBAP_CLIENT, BluetoothProfile.MAP_CLIENT);
+ mPrioritiesSupported = Arrays.asList(
+ CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0,
+ CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1
+ );
// mNumSupportedActiveConnections is a HashMap of mProfilesToConnect and the number of
// connections each profile supports currently.
mNumSupportedActiveConnections = new HashMap<>(mProfilesToConnect.size());
- for (Integer profile: mProfilesToConnect) {
+ for (Integer profile : mProfilesToConnect) {
switch (profile) {
case BluetoothProfile.HEADSET_CLIENT:
mNumSupportedActiveConnections.put(BluetoothProfile.HEADSET_CLIENT,
@@ -300,7 +315,7 @@
if (uuids != null) {
ParcelUuid[] uuidsToSend = new ParcelUuid[uuids.length];
for (int i = 0; i < uuidsToSend.length; i++) {
- uuidsToSend[i] = (ParcelUuid)uuids[i];
+ uuidsToSend[i] = (ParcelUuid) uuids[i];
}
setProfilePriorities(device, uuidsToSend, BluetoothProfile.PRIORITY_ON);
}
@@ -383,7 +398,8 @@
* Cleanup state and reinitialize whenever we connect to the PerUserCarService.
* This happens in init() and whenever the PerUserCarService is restarted on User Switch Events
*/
- private class UserServiceConnectionCallback implements PerUserCarServiceHelper.ServiceCallback {
+ @VisibleForTesting
+ class UserServiceConnectionCallback implements PerUserCarServiceHelper.ServiceCallback {
@Override
public void onServiceConnected(ICarUserService carUserService) {
if (mCarUserServiceAccessLock != null) {
@@ -415,11 +431,13 @@
}
} catch (RemoteException e) {
Log.e(TAG,
- "Remote Exception during closeBluetoothConnectionProxy(): " + e.getMessage());
+ "Remote Exception during closeBluetoothConnectionProxy(): "
+ + e.getMessage());
}
// Clean up information related to user who went background.
cleanupUserSpecificInfo();
}
+
@Override
public void onServiceDisconnected() {
if (DBG) {
@@ -442,6 +460,7 @@
* which acts as a top level Service running in the current user context.
* Also sets up the connection proxy objects required to communicate with the Bluetooth
* Profile Services.
+ *
* @return ICarBluetoothUserService running in current user
*/
private ICarBluetoothUserService setupBluetoothUserService() {
@@ -573,7 +592,8 @@
* Upon receiving the event that is of interest, initiate a connection attempt by calling
* the policy {@link BluetoothDeviceConnectionPolicy}
*/
- private class CarPropertyListener extends ICarPropertyEventListener.Stub {
+ @VisibleForTesting
+ class CarPropertyListener extends ICarPropertyEventListener.Stub {
@Override
public void onEvent(CarPropertyEvent event) throws RemoteException {
if (DBG) {
@@ -658,7 +678,9 @@
mProfileToConnectableDevicesMap = null;
mConnectionInFlight = null;
if (mBluetoothBroadcastReceiver != null) {
- mContext.unregisterReceiver(mBluetoothBroadcastReceiver);
+ if (mContext != null) {
+ mContext.unregisterReceiver(mBluetoothBroadcastReceiver);
+ }
mBluetoothBroadcastReceiver = null;
}
}
@@ -695,6 +717,31 @@
}
}
+ @VisibleForTesting
+ BroadcastReceiver getBluetoothBroadcastReceiver() {
+ return mBluetoothBroadcastReceiver;
+ }
+
+ @VisibleForTesting
+ UserServiceConnectionCallback getServiceCallback() {
+ return mServiceCallback;
+ }
+
+ @VisibleForTesting
+ CarPropertyListener getCarPropertyListener() {
+ return mCabinEventListener;
+ }
+
+ @VisibleForTesting
+ synchronized void setAllowReadWriteToSettings(boolean allowWrite) {
+ mAllowReadWriteToSettings = allowWrite;
+ }
+
+ @VisibleForTesting
+ BluetoothDevicesInfo getBluetoothDevicesInfo(int profile) {
+ return mProfileToConnectableDevicesMap.get(profile);
+ }
+
/**
* Resets the {@link #mProfileToConnectableDevicesMap} to a clean and empty slate.
*/
@@ -792,7 +839,6 @@
Log.w(TAG, "Bluetooth Adapter null");
return;
}
-
if (mBluetoothAdapter.isEnabled()) {
if (isDeviceMapEmpty()) {
if (DBG) {
@@ -1050,7 +1096,7 @@
BluetoothDevice deviceThatConnected = params.getBluetoothDevice();
if (DBG) {
Log.d(TAG, "Profile: " + profileToUpdate + " Connected: " + didConnect + " on "
- + deviceThatConnected.getName());
+ + deviceThatConnected);
}
// If the connection update is on a different profile or device (a very rare possibility),
@@ -1066,9 +1112,9 @@
if (mConnectionInFlight != null && mConnectionInFlight.getBluetoothDevice() != null) {
if (deviceThatConnected.equals(mConnectionInFlight.getBluetoothDevice()) == false) {
- Log.d(TAG, "Updating device: " + deviceThatConnected.getName()
+ Log.d(TAG, "Updating device: " + deviceThatConnected
+ " different from connection in flight: "
- + mConnectionInFlight.getBluetoothDevice().getName());
+ + mConnectionInFlight.getBluetoothDevice());
}
}
@@ -1150,6 +1196,7 @@
private synchronized void resetDeviceAvailableToConnect() {
for (BluetoothDevicesInfo devInfo : mProfileToConnectableDevicesMap.values()) {
devInfo.setDeviceAvailableToConnectLocked(true);
+ devInfo.resetDeviceIndex();
}
}
@@ -1168,12 +1215,13 @@
writer.print(
"Num of Paired devices: " + devInfo.getNumberOfPairedDevicesLocked() + "\t");
writer.print("Active Connections: " + devInfo.getNumberOfActiveConnectionsLocked());
+ writer.println("Num of paired devices: " + devInfo.getNumberOfPairedDevicesLocked());
writer.println();
List<BluetoothDevicesInfo.DeviceInfo> deviceInfoList = devInfo.getDeviceInfoList();
if (deviceInfoList != null) {
for (BluetoothDevicesInfo.DeviceInfo devicesInfo : deviceInfoList) {
if (devicesInfo.getBluetoothDevice() != null) {
- writer.print(devicesInfo.getBluetoothDevice().getName() + ":");
+ writer.print(devicesInfo.getBluetoothDevice() + ":");
writer.print(devicesInfo.getConnectionState() + "\t");
}
}
@@ -1212,6 +1260,9 @@
* @return true if the write was successful, false otherwise
*/
public synchronized boolean writeDeviceInfoToSettings(ConnectionParams params) {
+ if (!mAllowReadWriteToSettings) {
+ return false;
+ }
boolean writeSuccess = true;
Integer profileToUpdate = params.getBluetoothProfile();
@@ -1237,8 +1288,9 @@
// joinedDeviceNames has something like "22:22:33:44:55:AB,22:23:xx:xx:xx:xx"
// mac addresses of connectable devices separated by a delimiter
String joinedDeviceNames = sb.toString();
- Log.d(TAG, "Profile: " + profileToUpdate + " Writing: " + joinedDeviceNames);
-
+ if (DBG) {
+ Log.d(TAG, "Profile: " + profileToUpdate + " Writing: " + joinedDeviceNames);
+ }
long userId = ActivityManager.getCurrentUser();
switch (profileToUpdate) {
case BluetoothProfile.A2DP_SINK:
@@ -1297,6 +1349,9 @@
return false;
}
}
+ if (!mAllowReadWriteToSettings) {
+ return false;
+ }
// Read from Settings.Secure for the current user. There are 3 keys 1 each for Phone
// (HFP & PBAP), 1 for Music (A2DP) and 1 for Messaging device (MAP)
long userId = ActivityManager.getCurrentUser();
@@ -1349,16 +1404,82 @@
}
}
mProfileToConnectableDevicesMap.put(profile, devicesInfo);
+ // Check to see if there are any primary or secondary devices for this profile and
+ // update BluetoothDevicesInfo with the priority information.
+ for (int priority : mPrioritiesSupported) {
+ readAndTagDeviceWithPriorityFromSettings(profile, priority);
+ }
}
return true;
}
/**
+ * Read from Secure Settings if there are primary or secondary devices marked for this
+ * Bluetooth profile. If there are tagged devices, update the BluetoothDevicesInfo so the
+ * policy can prioritize those devices when making connection attempts.
+ *
+ * @param profile - Bluetooth Profile to check
+ * @param priority - Priority to check
+ */
+ private void readAndTagDeviceWithPriorityFromSettings(int profile, int priority) {
+ BluetoothDevicesInfo devicesInfo = mProfileToConnectableDevicesMap.get(profile);
+ if (devicesInfo == null) {
+ return;
+ }
+ if (!mCarBluetoothService.isPriorityDevicePresent(profile, priority)) {
+ // There is no device for this priority - either it hasn't been set or has been removed.
+ // So check if the policy has a device associated with this priority and remove it.
+ BluetoothDevice deviceToClear = devicesInfo.getBluetoothDeviceForPriorityLocked(
+ priority);
+ if (deviceToClear != null) {
+ if (DBG) {
+ Log.d(TAG, "Clearing priority for: " + deviceToClear.getAddress());
+ }
+ devicesInfo.removeBluetoothDevicePriorityLocked(deviceToClear);
+ }
+ } else {
+ // There is a device with the given priority for the given profile. Update the
+ // policy's records.
+ String deviceName = mCarBluetoothService.getDeviceNameWithPriority(profile,
+ priority);
+ if (deviceName != null) {
+ BluetoothDevice bluetoothDevice = getBondedDeviceWithGivenName(deviceName);
+ if (bluetoothDevice != null) {
+ if (DBG) {
+ Log.d(TAG, "Setting priority: " + priority + " for " + deviceName);
+ }
+ tagDeviceWithPriority(bluetoothDevice, profile, priority);
+ }
+ }
+ }
+ }
+
+ /**
+ * Tag a Bluetooth device with priority - Primary or Secondary. This only updates the policy's
+ * record (BluetoothDevicesInfo) of the priority information.
+ *
+ * @param device - BluetoothDevice to tag
+ * @param profile - BluetoothProfile to tag
+ * @param priority - Priority to tag with
+ */
+ @VisibleForTesting
+ void tagDeviceWithPriority(BluetoothDevice device, int profile, int priority) {
+ BluetoothDevicesInfo devicesInfo = mProfileToConnectableDevicesMap.get(profile);
+ if (device != null) {
+ if (DBG) {
+ Log.d(TAG, "Profile: " + profile + " : " + device + " Priority: " + priority);
+ }
+ devicesInfo.setBluetoothDevicePriorityLocked(device, priority);
+ }
+ }
+
+ /**
* Given the device name, find the corresponding {@link BluetoothDevice} from the list of
* Bonded devices.
*
* @param name Bluetooth Device name
*/
+ @Nullable
private BluetoothDevice getBondedDeviceWithGivenName(String name) {
if (mBluetoothAdapter == null) {
if (DBG) {
diff --git a/service/src/com/android/car/BluetoothDevicesInfo.java b/service/src/com/android/car/BluetoothDevicesInfo.java
index a383f04..4a0fee9 100644
--- a/service/src/com/android/car/BluetoothDevicesInfo.java
+++ b/service/src/com/android/car/BluetoothDevicesInfo.java
@@ -18,6 +18,7 @@
import android.annotation.Nullable;
import android.bluetooth.BluetoothDevice;
+import android.car.CarBluetoothManager;
import android.util.Log;
import java.util.List;
@@ -47,6 +48,7 @@
private static final String TAG = "CarBluetoothDevicesInfo";
private static final boolean DBG = false;
private final int DEVICE_NOT_FOUND = -1;
+ private final int DEVICE_PRIORITY_UNDEFINED = -1;
// The device list and the connection state information together have all the information
// that is required to know which device(s) to connect to, when we need to connect/
private List<DeviceInfo> mDeviceInfoList;
@@ -98,10 +100,12 @@
private BluetoothDevice mBluetoothDevice;
private int mConnectionState;
+ private int mDevicePriority;
public DeviceInfo(BluetoothDevice device, int state) {
mBluetoothDevice = device;
mConnectionState = state;
+ mDevicePriority = DEVICE_PRIORITY_UNDEFINED;
}
public void setConnectionState(int state) {
@@ -115,6 +119,14 @@
public BluetoothDevice getBluetoothDevice() {
return mBluetoothDevice;
}
+
+ public void setBluetoothDevicePriority(int priority) {
+ mDevicePriority = priority;
+ }
+
+ public int getBluetoothDevicePriority() {
+ return mDevicePriority;
+ }
}
public BluetoothDevicesInfo(int profile) {
@@ -128,6 +140,87 @@
}
/**
+ * Set the priority of the device with the given priority level
+ *
+ * @param deviceToTag - BluetoothDevice to set the priority for
+ * @param priority - Priority to set
+ */
+
+ public void setBluetoothDevicePriorityLocked(BluetoothDevice deviceToTag, int priority) {
+ /*if (priority >= mConnectionInfo.mNumConnectionsSupported) {
+ if (DBG) {
+ Log.d(TAG, "Priority cannot exceed number of connections supported");
+ }
+ return;
+ }*/
+ // if there is a device already set to that priority, unseat that device
+ BluetoothDevice oldDeviceWithPriority = getBluetoothDeviceForPriorityLocked(priority);
+ if (oldDeviceWithPriority != null) {
+ if (DBG) {
+ Log.d(TAG, "Unsetting priority " + priority + " on " + oldDeviceWithPriority);
+ }
+ removeBluetoothDevicePriorityLocked(oldDeviceWithPriority);
+ }
+ // Tag the new device with the given priority
+ DeviceInfo newDeviceInfoWithPriority = findDeviceInfoInListLocked(deviceToTag);
+ if (newDeviceInfoWithPriority == null) {
+ if (DBG) {
+ Log.d(TAG, "setBluetoothDevicePriorityLocked():Unknown and unpaired device");
+ }
+ return;
+ }
+ if (DBG) {
+ Log.d(TAG, "Setting priority " + priority + " to "
+ + newDeviceInfoWithPriority.mBluetoothDevice);
+ }
+ newDeviceInfoWithPriority.setBluetoothDevicePriority(priority);
+ // Update the position of the device in the device Queue
+ moveDeviceToPrioritySlotsLocked(newDeviceInfoWithPriority, priority);
+ }
+
+ /**
+ * Clear the priority of the given device.
+ *
+ * @param deviceToUntag - BluetoothDevice to untag
+ */
+ public void removeBluetoothDevicePriorityLocked(BluetoothDevice deviceToUntag) {
+ DeviceInfo deviceInfo = findDeviceInfoInListLocked(deviceToUntag);
+ deviceInfo.setBluetoothDevicePriority(DEVICE_PRIORITY_UNDEFINED);
+ }
+
+ /**
+ * Returns the number of devices that have been tagged as priority devices.
+ * If there is a device that is tagged as a Secondary device, then the number of tagged devices
+ * is 2, even if there is no primary device.
+ *
+ * @return - Number of Tagged devices Ex: Only Primary - 1, Primary and/or Secondary - 2
+ */
+ public int getNumberOfTaggedDevicesLocked() {
+ int numberOfTaggedDevices = 0;
+ if (getBluetoothDeviceForPriorityLocked(
+ CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1) != null) {
+ return CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1 + 1;
+ } else if (getBluetoothDeviceForPriorityLocked(
+ CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0) != null) {
+ return CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0 + 1;
+ }
+ return numberOfTaggedDevices;
+ }
+
+ /**
+ * Returns the device that has the passed priority
+ */
+ public BluetoothDevice getBluetoothDeviceForPriorityLocked(int priority) {
+ BluetoothDevice device = null;
+ for (DeviceInfo deviceInfo : mDeviceInfoList) {
+ if (deviceInfo.mDevicePriority == priority) {
+ return deviceInfo.mBluetoothDevice;
+ }
+ }
+ return device;
+ }
+
+ /**
* Get the position of the given device in the list of connectable devices for this profile.
*
* @param device - {@link BluetoothDevice}
@@ -169,6 +262,13 @@
return isPresent;
}
+ /**
+ * Iterate through the {@link BluetoothDevicesInfo#mDeviceInfoList} and find the
+ * {@link DeviceInfo} with the given {@link BluetoothDevice}
+ *
+ * @param device - {@link BluetoothDevice} to look for
+ * @return - {@link DeviceInfo} that contains the passed {@link BluetoothDevice}
+ */
private DeviceInfo findDeviceInfoInListLocked(@Nullable BluetoothDevice device) {
if (device == null) {
return null;
@@ -180,6 +280,7 @@
}
return null;
}
+
/**
* Get the current list of connectable devices for this profile.
*
@@ -261,10 +362,10 @@
* @param device - device to get the bluetooth connection state for
* @return - Connection State. If passed device is null, returns DEVICE_NOT_FOUND.
*/
- public int getConnectionStateLocked(BluetoothDevice device) {
+ public int getCurrentConnectionStateLocked(BluetoothDevice device) {
int state = DEVICE_NOT_FOUND;
if (device == null) {
- Log.e(TAG, "getConnectionStateLocked() device null");
+ Log.e(TAG, "getCurrentConnectionStateLocked() device null");
return state;
}
@@ -282,6 +383,39 @@
}
/**
+ * Returns the device that is currently in the middle of a connection attempt.
+ *
+ * @return BluetoothDevice that is connecting, null if no device is connecting
+ */
+ public BluetoothDevice getConnectingDeviceLocked() {
+ for (DeviceInfo devInfo : mDeviceInfoList) {
+ if (devInfo.getConnectionState() == BluetoothProfile.STATE_CONNECTING) {
+ return devInfo.getBluetoothDevice();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a list of connected devices for this profile.
+ *
+ * @return - List of connected devices
+ */
+ public List<BluetoothDevice> getConnectedDevicesLocked() {
+ List<BluetoothDevice> devices = new ArrayList<>();
+ for (DeviceInfo devInfo : mDeviceInfoList) {
+ if (devInfo.getConnectionState() == BluetoothProfile.STATE_CONNECTED) {
+ devices.add(devInfo.getBluetoothDevice());
+ }
+ }
+ if (DBG) {
+ Log.d(TAG, "Active Connections: " + getNumberOfActiveConnectionsLocked());
+ Log.d(TAG, "Connected devices Size: " + devices.size());
+ }
+ return devices;
+ }
+
+ /**
* Remove a device from the list. Used when a device is unpaired
*
* @param dev - device to remove from the list.
@@ -322,6 +456,13 @@
public BluetoothDevice getNextDeviceInQueueLocked() {
BluetoothDevice device = null;
int numberOfPairedDevices = getNumberOfPairedDevicesLocked();
+ // iterate till we find a disconnected device
+ while (mConnectionInfo.mDeviceIndex < numberOfPairedDevices &&
+ mDeviceInfoList.get(mConnectionInfo.mDeviceIndex).getConnectionState()
+ != BluetoothProfile.STATE_DISCONNECTED) {
+ mConnectionInfo.mDeviceIndex++;
+ }
+
if (mConnectionInfo.mDeviceIndex >= numberOfPairedDevices) {
if (DBG) {
Log.d(TAG,
@@ -345,6 +486,7 @@
mConnectionInfo.mDeviceIndex = 0;
return null;
}
+
device = mDeviceInfoList.get(mConnectionInfo.mDeviceIndex).mBluetoothDevice;
if (DBG) {
Log.d(TAG, "Getting device " + mConnectionInfo.mDeviceIndex + " from list: "
@@ -373,7 +515,6 @@
if (DBG) {
Log.d(TAG, mConnectionInfo.mProfile + " connected to " + device);
}
- // b/34722344 - TODO
// Get the position of this device in the device list maintained for this profile.
int positionInQ = getPositionInListLocked(device);
if (DBG) {
@@ -386,13 +527,15 @@
addDeviceLocked(device);
positionInQ = mDeviceInfoList.size() - 1;
} else if (positionInQ != mConnectionInfo.mDeviceIndex) {
- // This will happen if auto-connect request a connect on a device from its list,
- // but the device that connected was different. Maybe there was another requestor
- // and the Bluetooth services chose to honor the other request. What we do here,
- // is to make sure we note which device connected and not assume that the device
- // that connected is the device we requested. The ultimate goal of the policy is
- // to remember which devices connected on which profile (regardless of the origin
- // of the connection request) so it knows which device to connect the next time.
+ /*
+ This will happen if auto-connect requests to connect on a device from its list,
+ but the device that connected was different. Maybe there was another requestor
+ and the Bluetooth services chose to honor the other request. What we do here,
+ is to make sure we note which device connected and not assume that the device
+ that connected is the device we requested. The ultimate goal of the policy is
+ to remember which devices connected on which profile (regardless of the origin
+ of the connection request) so it knows which device to connect the next time.
+ */
if (DBG) {
Log.d(TAG, "Different device connected: " + device + " CurrIndex: "
+ mConnectionInfo.mDeviceIndex);
@@ -405,15 +548,26 @@
// to the front of the list, the next time auto connect triggers, this will be the
// device that the policy will try to connect on for this profile.
if (positionInQ != 0) {
- moveToFrontLocked(positionInQ);
+ moveDeviceToQueueFrontLocked(positionInQ);
// reset the device Index back to the first in the Queue
//mConnectionInfo.mDeviceIndex = 0;
}
+
+ if (getCurrentConnectionStateLocked(device) != BluetoothProfile.STATE_CONNECTED) {
+ mConnectionInfo.mNumActiveConnections++;
+ if (DBG) {
+ Log.d(TAG,
+ "Incrementing Active Connections "
+ + mConnectionInfo.mNumActiveConnections);
+ }
+ }
setConnectionStateLocked(device, BluetoothProfile.STATE_CONNECTED);
// Reset the retry count
mConnectionInfo.mRetryAttempt = 0;
- mConnectionInfo.mNumActiveConnections++;
- mConnectionInfo.mDeviceIndex++;
+
+ if (getConnectingDeviceLocked() == null) {
+ mConnectionInfo.mDeviceIndex++;
+ }
if (DBG) {
Log.d(TAG,
"Profile: " + mConnectionInfo.mProfile + " Number of Active Connections: "
@@ -424,33 +578,72 @@
if (DBG) {
Log.d(TAG, "Connection fail or Disconnected");
}
- // only if the given device was already connected decrement this.
- if (getConnectionStateLocked(device) == BluetoothProfile.STATE_CONNECTED) {
+ // Decrement Number of Active Connections only if the device is already connected.
+ if (getCurrentConnectionStateLocked(device) == BluetoothProfile.STATE_CONNECTED) {
mConnectionInfo.mNumActiveConnections--;
+ if (DBG) {
+ Log.d(TAG, "Decrementing Active Connections "
+ + mConnectionInfo.mNumActiveConnections);
+ }
}
setConnectionStateLocked(device, BluetoothProfile.STATE_DISCONNECTED);
- if (!retry) {
- mConnectionInfo.mDeviceIndex++;
- if (DBG) {
- Log.d(TAG, "Moving to device: " + mConnectionInfo.mDeviceIndex);
+
+ // Update the mDeviceIndex only when there is no other device currently in the middle
+ // of a connection attempt. This is to safeguard against disconnect intents coming in
+ // for devices other than the one that the policy is currently trying to connect.
+ if (getConnectingDeviceLocked() == null) {
+ if (!retry) {
+ mConnectionInfo.mDeviceIndex++;
+ if (DBG) {
+ Log.d(TAG, "Moving to device: " + mConnectionInfo.mDeviceIndex);
+ }
+ // Reset the retry count
+ mConnectionInfo.mRetryAttempt = 0;
+ } else {
+ if (DBG) {
+ Log.d(TAG, "Staying with the same device - retrying: "
+ + mConnectionInfo.mDeviceIndex);
+ }
}
- // Reset the retry count
- mConnectionInfo.mRetryAttempt = 0;
} else {
- if (DBG) {
- Log.d(TAG, "Staying with the same device - retrying: "
- + mConnectionInfo.mDeviceIndex);
+ BluetoothDevice connectingDevice = getConnectingDeviceLocked();
+ if (connectingDevice != null) {
+ if (DBG) {
+ Log.d(TAG, "Not moving to next device. " + connectingDevice
+ + " still connecting");
+ }
+ } else {
+ Log.e(TAG, "Unexpected. Status = connecting, but connecting device = null");
}
}
}
}
/**
- * Move the item in the given position to the front of the list and push the rest down.
+ * Move the given device to its priority slot
*
- * @param position - position of the device to move from
+ * @param deviceInfo - DeviceInfo to move
+ * @param priority - Priority of the device in the list
*/
- private void moveToFrontLocked(int position) {
+ private void moveDeviceToPrioritySlotsLocked(DeviceInfo deviceInfo, int priority) {
+ if (DBG) {
+ Log.d(TAG, "Moving " + deviceInfo.mBluetoothDevice + " to " + priority);
+ }
+ mDeviceInfoList.remove(deviceInfo);
+ mDeviceInfoList.add(priority, deviceInfo);
+ }
+
+ /**
+ * Move the item in the given position to the front of the queue and push the rest down.
+ *
+ * @param position - current position of the device that it is moving from
+ */
+ private void moveDeviceToQueueFrontLocked(int position) {
+ int topOfList = getNumberOfTaggedDevicesLocked();
+ // If the device is a primary or secondary, its position is fixed.
+ if (position <= topOfList) {
+ return;
+ }
DeviceInfo deviceInfo = mDeviceInfoList.get(position);
if (deviceInfo.mBluetoothDevice == null) {
if (DBG) {
@@ -459,7 +652,11 @@
return;
}
mDeviceInfoList.remove(position);
- mDeviceInfoList.add(0, deviceInfo);
+ // Top of the list to which a device can be moved depends on the number of tagged devices
+ // If there is a dedicated Primary device, then the newly connected device can only be moved
+ // to the second position, since the primary device always occupies the first position.
+ // Hence the topOfList is the first position after the tagged devices.
+ mDeviceInfoList.add(topOfList, deviceInfo);
}
/**
@@ -524,8 +721,14 @@
mConnectionInfo.mNumActiveConnections < mConnectionInfo.mNumConnectionsSupported) {
return true;
}
- return false;
+ if (DBG) {
+ Log.d(TAG, "Connected devices for profile " + mConnectionInfo.mProfile);
+ for (BluetoothDevice device : getConnectedDevicesLocked()) {
+ Log.d(TAG, device.getAddress());
+ }
+ }
+ return false;
}
/**
@@ -537,6 +740,10 @@
return mConnectionInfo.mNumActiveConnections;
}
+ public void resetDeviceIndex() {
+ mConnectionInfo.mDeviceIndex = 0;
+ }
+
/**
* Reset the connection related bookkeeping information.
* Called on a BluetoothAdapter Off to clean slate
@@ -558,5 +765,4 @@
}
resetConnectionInfoLocked();
}
-
}
diff --git a/service/src/com/android/car/CarAudioAttributesUtil.java b/service/src/com/android/car/CarAudioAttributesUtil.java
index 54ed2c6..64d2571 100644
--- a/service/src/com/android/car/CarAudioAttributesUtil.java
+++ b/service/src/com/android/car/CarAudioAttributesUtil.java
@@ -55,6 +55,9 @@
case CarAudioManager.CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE:
return createAudioAttributes(AudioAttributes.CONTENT_TYPE_SPEECH,
AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE);
+ case CarAudioManager.CAR_AUDIO_USAGE_RINGTONE:
+ return createAudioAttributes(AudioAttributes.CONTENT_TYPE_SONIFICATION,
+ AudioAttributes.USAGE_NOTIFICATION_RINGTONE);
case CarAudioManager.CAR_AUDIO_USAGE_VOICE_CALL:
return createAudioAttributes(AudioAttributes.CONTENT_TYPE_SPEECH,
AudioAttributes.USAGE_VOICE_COMMUNICATION);
@@ -119,6 +122,8 @@
}
case AudioAttributes.USAGE_VOICE_COMMUNICATION:
return CarAudioManager.CAR_AUDIO_USAGE_VOICE_CALL;
+ case AudioAttributes.USAGE_NOTIFICATION_RINGTONE:
+ return CarAudioManager.CAR_AUDIO_USAGE_RINGTONE;
case AudioAttributes.USAGE_ALARM:
return CarAudioManager.CAR_AUDIO_USAGE_ALARM;
case AudioAttributes.USAGE_NOTIFICATION:
diff --git a/service/src/com/android/car/CarAudioService.java b/service/src/com/android/car/CarAudioService.java
index d161883..f56b105 100644
--- a/service/src/com/android/car/CarAudioService.java
+++ b/service/src/com/android/car/CarAudioService.java
@@ -429,6 +429,7 @@
if (audioPolicy != null) {
mAudioManager.unregisterAudioPolicyAsync(audioPolicy);
}
+ mVolumeService.release();
}
public synchronized void setAudioContextChangeListener(Looper looper,
@@ -707,7 +708,6 @@
private void doHandleCarFocusChange() {
int newFocusState = AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_INVALID;
- FocusState currentState;
AudioFocusInfo topInfo;
boolean systemSoundActive = false;
synchronized (mLock) {
@@ -729,7 +729,6 @@
newFocusState = mFocusReceived.focusState;
}
mCurrentFocusState = mFocusReceived;
- currentState = mFocusReceived;
mFocusReceived = null;
if (mLastFocusRequestToCar != null &&
(mLastFocusRequestToCar.focusRequest ==
@@ -764,22 +763,22 @@
}
switch (newFocusState) {
case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_GAIN:
- doHandleFocusGainFromCar(currentState, topInfo, systemSoundActive);
+ doHandleFocusGainFromCar(mCurrentFocusState, topInfo, systemSoundActive);
break;
case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_GAIN_TRANSIENT:
- doHandleFocusGainTransientFromCar(currentState, topInfo, systemSoundActive);
+ doHandleFocusGainTransientFromCar(mCurrentFocusState, topInfo, systemSoundActive);
break;
case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS:
- doHandleFocusLossFromCar(currentState, topInfo);
+ doHandleFocusLossFromCar(mCurrentFocusState, topInfo);
break;
case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT:
- doHandleFocusLossTransientFromCar(currentState);
+ doHandleFocusLossTransientFromCar(mCurrentFocusState);
break;
case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT_CAN_DUCK:
- doHandleFocusLossTransientCanDuckFromCar(currentState);
+ doHandleFocusLossTransientCanDuckFromCar(mCurrentFocusState);
break;
case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT_EXLCUSIVE:
- doHandleFocusLossTransientExclusiveFromCar(currentState);
+ doHandleFocusLossTransientExclusiveFromCar(mCurrentFocusState);
break;
}
}
@@ -827,15 +826,18 @@
Log.d(TAG_FOCUS, "doHandleFocusLossFromCar current:" + currentState +
" top:" + dumpAudioFocusInfo(topInfo));
}
- boolean shouldRequestProxyFocus = false;
- if ((currentState.externalFocus &
- AudioHalService.VEHICLE_AUDIO_EXT_FOCUS_CAR_PERMANENT_FLAG) != 0) {
- shouldRequestProxyFocus = true;
- }
if (isFocusFromCarProxy(topInfo)) {
// already car proxy is top. Nothing to do.
return;
- } else if (!isFocusFromCarServiceBottom(topInfo)) {
+ }
+ boolean shouldRequestProxyFocus = false;
+ if ((currentState.externalFocus &
+ AudioHalService.VEHICLE_AUDIO_EXT_FOCUS_CAR_PERMANENT_FLAG) != 0) {
+ // If car is playing something persistent, the car proxy should have focus
+ shouldRequestProxyFocus = true;
+ }
+ if (!isFocusFromCarServiceBottom(topInfo)) {
+ // If a car source was being ducked, it should get the primary focus back
shouldRequestProxyFocus = true;
}
if (shouldRequestProxyFocus) {
@@ -1798,8 +1800,8 @@
public static FocusState create(int[] state) {
return create(state[AudioHalService.FOCUS_STATE_ARRAY_INDEX_STATE],
- state[AudioHalService.FOCUS_STATE_ARRAY_INDEX_STREAMS],
- state[AudioHalService.FOCUS_STATE_ARRAY_INDEX_EXTERNAL_FOCUS]);
+ state[AudioHalService.FOCUS_STATE_ARRAY_INDEX_STREAMS],
+ state[AudioHalService.FOCUS_STATE_ARRAY_INDEX_EXTERNAL_FOCUS]);
}
public static FocusState STATE_LOSS =
diff --git a/service/src/com/android/car/CarBluetoothService.java b/service/src/com/android/car/CarBluetoothService.java
index c1adea9..33d19c5 100644
--- a/service/src/com/android/car/CarBluetoothService.java
+++ b/service/src/com/android/car/CarBluetoothService.java
@@ -15,7 +15,27 @@
*/
package com.android.car;
+import static android.car.settings.CarSettings.Secure
+ .KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_0;
+import static android.car.settings.CarSettings.Secure
+ .KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_1;
+import static android.car.settings.CarSettings.Secure
+ .KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_0;
+import static android.car.settings.CarSettings.Secure
+ .KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_1;
+import static android.car.settings.CarSettings.Secure
+ .KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_0;
+import static android.car.settings.CarSettings.Secure
+ .KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_1;
+
+import android.app.ActivityManager;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.car.CarBluetoothManager;
+import android.car.ICarBluetooth;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
import android.util.Log;
import java.io.PrintWriter;
@@ -28,17 +48,18 @@
* connect for a specific profile.
*/
-public class CarBluetoothService implements CarServiceBase {
+public class CarBluetoothService extends ICarBluetooth.Stub implements CarServiceBase {
private static final String TAG = "CarBluetoothService";
private final Context mContext;
private final BluetoothDeviceConnectionPolicy mBluetoothDeviceConnectionPolicy;
+ private static final boolean DBG = false;
public CarBluetoothService(Context context, CarCabinService carCabinService,
CarSensorService carSensorService, PerUserCarServiceHelper userSwitchService) {
mContext = context;
mBluetoothDeviceConnectionPolicy = BluetoothDeviceConnectionPolicy.create(mContext,
- carCabinService, carSensorService, userSwitchService);
+ carCabinService, carSensorService, userSwitchService, this);
}
@Override
@@ -51,6 +72,173 @@
mBluetoothDeviceConnectionPolicy.release();
}
+ /**
+ * Set the Auto connect priority for a paired Bluetooth Device.
+ * For example, if a device is tagged as a Primary device for a supported Bluetooth Profile,
+ * every new Auto Connect attempt would start with trying to connect to *that* device.
+ * This priority is set at a Bluetooth profile granularity
+ *
+ * @param deviceToSet - Device to set priority (Tag)
+ * @param profileToSet - BluetoothProfile to set priority for.
+ * @param priorityToSet - What priority level to set to
+ * @hide
+ */
+ public void setBluetoothDeviceConnectionPriority(BluetoothDevice deviceToSet, int profileToSet,
+ int priorityToSet) {
+ setBluetoothDeviceConnectionPriority(deviceToSet.getAddress(), profileToSet, priorityToSet);
+ }
+
+ public void setBluetoothDeviceConnectionPriority(String deviceAddress, int profileToSet,
+ int priorityToSet) {
+ // Check if the caller has Bluetooth Admin Permissions
+ enforceBluetoothAdminPermission();
+ if (priorityToSet == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1) {
+ if (!isPriorityDevicePresent(profileToSet,
+ CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)) {
+ Log.e(TAG, "Secondary Device not allowed without a primary device");
+ return;
+ }
+ }
+ // Write the priority preference to Secure settings. The Bluetooth device connection policy
+ // will look up the Settings when it initiates a connection
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ getKeyForProfile(profileToSet, priorityToSet), deviceAddress,
+ ActivityManager.getCurrentUser());
+
+ }
+
+ /**
+ * Unset the Auto connect priority for the given profile
+ *
+ * @param profileToClear - Profile to unset priority
+ * @param priorityToClear - Which priority to clear (Primary or Secondary)
+ * @hide
+ */
+ public void clearBluetoothDeviceConnectionPriority(int profileToClear, int priorityToClear) {
+ // Check if the caller has Bluetooth Admin F@Permissions
+ enforceBluetoothAdminPermission();
+ if (priorityToClear == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0) {
+ if (isPriorityDevicePresent(profileToClear,
+ CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1)) {
+ Log.e(TAG, "Please remove Secondary device before removing Primary Device");
+ return;
+ }
+ }
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ getKeyForProfile(profileToClear, priorityToClear),
+ CarBluetoothManager.BLUETOOTH_NO_PRIORITY_DEVICE,
+ ActivityManager.getCurrentUser());
+ }
+
+ /**
+ * Returns if there is a device that has been tagged with the given priority for the given
+ * profile.
+ *
+ * @param profile - BluetoothProfile
+ * @param priorityToCheck - Priority to check
+ * @return true if there is a device present with the given priority, false if not
+ */
+ public boolean isPriorityDevicePresent(int profile, int priorityToCheck) {
+ String deviceName = getDeviceNameWithPriority(profile, priorityToCheck);
+ if (deviceName != null && !deviceName.equalsIgnoreCase(
+ CarBluetoothManager.BLUETOOTH_NO_PRIORITY_DEVICE)) {
+ return true;
+ } else {
+ if (DBG) {
+ Log.d(TAG,
+ "No device present for priority: " + priorityToCheck + " profile: "
+ + profile);
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Returns the Bluetooth device address as a String that has been tagged with the given priority
+ * for the given profile.
+ *
+ * @param profile - BluetoothProfile
+ * @param priorityToCheck - Priority to check
+ * @return BluetoothDevice address if present, null if absent
+ */
+ public String getDeviceNameWithPriority(int profile, int priorityToCheck) {
+ String keyToQuery = null;
+ String deviceName = null;
+ enforceBluetoothAdminPermission();
+ switch (profile) {
+ case BluetoothProfile.A2DP_SINK:
+ keyToQuery = (priorityToCheck
+ == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)
+ ? KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_0
+ : KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_1;
+ break;
+ case BluetoothProfile.HEADSET_CLIENT:
+ case BluetoothProfile.PBAP_CLIENT:
+ keyToQuery = (priorityToCheck
+ == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)
+ ? KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_0
+ : KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_1;
+ break;
+ case BluetoothProfile.MAP_CLIENT:
+ keyToQuery = (priorityToCheck
+ == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)
+ ? KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_0
+ : KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_1;
+ break;
+ default:
+ if (DBG) {
+ Log.d(TAG, "Unknown Bluetooth profile");
+ }
+ }
+ if (keyToQuery != null) {
+ deviceName = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ keyToQuery, (int) ActivityManager.getCurrentUser());
+ }
+ return deviceName;
+ }
+
+ private void enforceBluetoothAdminPermission() {
+ if (mContext != null
+ && PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.BLUETOOTH_ADMIN)) {
+ return;
+ }
+ if (mContext == null) {
+ Log.e(TAG, "CarBluetoothPrioritySettings does not have a Context");
+ }
+ throw new SecurityException("requires permission " + android.Manifest.permission.BLUETOOTH_ADMIN);
+ }
+
+ private String getKeyForProfile(int profile, int priority) {
+ String keyToLookup = null;
+ switch (profile) {
+ case BluetoothProfile.A2DP_SINK:
+ keyToLookup = (priority
+ == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)
+ ? KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_0
+ : KEY_BLUETOOTH_AUTOCONNECT_MUSIC_DEVICE_PRIORITY_1;
+ break;
+ case BluetoothProfile.MAP_CLIENT:
+ keyToLookup = (priority
+ == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)
+ ? KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_0
+ : KEY_BLUETOOTH_AUTOCONNECT_MESSAGING_DEVICE_PRIORITY_1;
+ break;
+ case BluetoothProfile.PBAP_CLIENT:
+ // fall through
+ case BluetoothProfile.HEADSET_CLIENT:
+ keyToLookup = (priority
+ == CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0)
+ ? KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_0
+ : KEY_BLUETOOTH_AUTOCONNECT_PHONE_DEVICE_PRIORITY_1;
+ break;
+ default:
+ Log.e(TAG, "Unsupported Bluetooth profile to set priority to");
+ break;
+ }
+ return keyToLookup;
+ }
+
@Override
public synchronized void dump(PrintWriter writer) {
mBluetoothDeviceConnectionPolicy.dump(writer);
diff --git a/service/src/com/android/car/CarDiagnosticService.java b/service/src/com/android/car/CarDiagnosticService.java
index 88e7e34..809439c 100644
--- a/service/src/com/android/car/CarDiagnosticService.java
+++ b/service/src/com/android/car/CarDiagnosticService.java
@@ -19,11 +19,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.car.Car;
-import android.car.annotation.FutureFeature;
-import android.car.hardware.CarDiagnosticEvent;
-import android.car.hardware.CarDiagnosticManager;
-import android.car.hardware.ICarDiagnostic;
-import android.car.hardware.ICarDiagnosticEventListener;
+import android.car.diagnostic.CarDiagnosticEvent;
+import android.car.diagnostic.CarDiagnosticManager;
+import android.car.diagnostic.ICarDiagnostic;
+import android.car.diagnostic.ICarDiagnosticEventListener;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
@@ -35,7 +34,6 @@
import com.android.car.hal.DiagnosticHalService;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
@@ -46,7 +44,6 @@
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
-@FutureFeature
/** @hide */
public class CarDiagnosticService extends ICarDiagnostic.Stub
implements CarServiceBase, DiagnosticHalService.DiagnosticListener {
@@ -81,7 +78,8 @@
public CarDiagnosticService(Context context, DiagnosticHalService diagnosticHal) {
mContext = context;
mDiagnosticHal = diagnosticHal;
- mDiagnosticReadPermission = new CarPermission(mContext, Car.PERMISSION_CAR_DIAGNOSTIC_READ);
+ mDiagnosticReadPermission = new CarPermission(mContext,
+ Car.PERMISSION_CAR_DIAGNOSTIC_READ_ALL);
mDiagnosticClearPermission = new CarPermission(mContext,
Car.PERMISSION_CAR_DIAGNOSTIC_CLEAR);
}
@@ -395,18 +393,18 @@
return getDiagnosticHal().getDiagnosticCapabilities().isLiveFrameSupported();
}
- public boolean isFreezeFrameSupported() {
+ public boolean isFreezeFrameNotificationSupported() {
return getDiagnosticHal().getDiagnosticCapabilities().isFreezeFrameSupported();
}
- public boolean isFreezeFrameTimestampSupported() {
+ public boolean isGetFreezeFrameSupported() {
DiagnosticCapabilities diagnosticCapabilities =
getDiagnosticHal().getDiagnosticCapabilities();
return diagnosticCapabilities.isFreezeFrameInfoSupported() &&
diagnosticCapabilities.isFreezeFrameSupported();
}
- public boolean isFreezeFrameClearSupported() {
+ public boolean isClearFreezeFramesSupported() {
DiagnosticCapabilities diagnosticCapabilities =
getDiagnosticHal().getDiagnosticCapabilities();
return diagnosticCapabilities.isFreezeFrameClearSupported() &&
@@ -500,12 +498,9 @@
@Override
public boolean equals(Object o) {
- if (o instanceof CarDiagnosticService.DiagnosticClient
- && mListener.asBinder()
- == ((CarDiagnosticService.DiagnosticClient) o).mListener.asBinder()) {
- return true;
- }
- return false;
+ return o instanceof DiagnosticClient
+ && mListener.asBinder()
+ == ((DiagnosticClient) o).mListener.asBinder();
}
boolean isHoldingListenerBinder(IBinder listenerBinder) {
@@ -540,16 +535,12 @@
}
void dispatchDiagnosticUpdate(List<CarDiagnosticEvent> events) {
- if (events.size() == 0) {
- return;
- }
- if (mActive) {
+ if (events.size() != 0 && mActive) {
try {
mListener.onDiagnosticEvents(events);
} catch (RemoteException e) {
//ignore. crash will be handled by death handler
}
- } else {
}
}
diff --git a/service/src/com/android/car/CarProjectionService.java b/service/src/com/android/car/CarProjectionService.java
index 89ee8b0..5eb453f 100644
--- a/service/src/com/android/car/CarProjectionService.java
+++ b/service/src/com/android/car/CarProjectionService.java
@@ -149,7 +149,7 @@
}
@Override
- public void regsiterProjectionListener(ICarProjectionCallback listener, int filter) {
+ public void registerProjectionListener(ICarProjectionCallback listener, int filter) {
synchronized (this) {
ListenerInfo info = (ListenerInfo) mAllListeners.getBinderInterface(listener);
if (info == null) {
@@ -163,7 +163,7 @@
}
@Override
- public void unregsiterProjectionListener(ICarProjectionCallback listener) {
+ public void unregisterProjectionListener(ICarProjectionCallback listener) {
synchronized (this) {
mAllListeners.removeBinder(listener);
}
@@ -204,7 +204,7 @@
@Override
public void onBinderDeath(
BinderInterfaceContainer.BinderInterface<ICarProjectionCallback> bInterface) {
- unregsiterProjectionListener(bInterface.binderInterface);
+ unregisterProjectionListener(bInterface.binderInterface);
}
@Override
diff --git a/service/src/com/android/car/CarSensorEventFactory.java b/service/src/com/android/car/CarSensorEventFactory.java
index fbae0ed..6497e16 100644
--- a/service/src/com/android/car/CarSensorEventFactory.java
+++ b/service/src/com/android/car/CarSensorEventFactory.java
@@ -17,29 +17,50 @@
package com.android.car;
import android.car.hardware.CarSensorEvent;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
//TODO add memory pool and recycling
public class CarSensorEventFactory {
public static CarSensorEvent createBooleanEvent(int sensorType, long timestamp,
boolean value) {
- CarSensorEvent event = new CarSensorEvent(sensorType, timestamp, 0, 1);
+ CarSensorEvent event = new CarSensorEvent(sensorType, timestamp, 0, 1, 0);
event.intValues[0] = value ? 1 : 0;
return event;
}
public static CarSensorEvent createIntEvent(int sensorType, long timestamp, int value) {
- CarSensorEvent event = new CarSensorEvent(sensorType, timestamp, 0, 1);
+ CarSensorEvent event = new CarSensorEvent(sensorType, timestamp, 0, 1, 0);
event.intValues[0] = value;
return event;
}
public static CarSensorEvent createFloatEvent(int sensorType, long timestamp, float value) {
- CarSensorEvent event = new CarSensorEvent(sensorType, timestamp, 1, 0);
+ CarSensorEvent event = new CarSensorEvent(sensorType, timestamp, 1, 0, 0);
event.floatValues[0] = value;
return event;
}
+ public static CarSensorEvent createComplexEvent(int sensorType, long timestamp,
+ VehiclePropValue v) {
+ int numFloats = v.value.floatValues.size();
+ int numInts = v.value.int32Values.size();
+ int numLongs = v.value.int64Values.size();
+ CarSensorEvent event = new CarSensorEvent(sensorType, timestamp, numFloats, numInts,
+ numLongs);
+ // Copy arraylist elements into final arrays
+ for (int i=0; i<numFloats; i++) {
+ event.floatValues[i] = v.value.floatValues.get(i);
+ }
+ for (int i=0; i<numInts; i++) {
+ event.intValues[i] = v.value.int32Values.get(i);
+ }
+ for (int i=0; i<numLongs; i++) {
+ event.longValues[i] = v.value.int64Values.get(i);
+ }
+ return event;
+ }
+
public static void returnToPool(CarSensorEvent event) {
//TODO
}
diff --git a/service/src/com/android/car/CarSensorService.java b/service/src/com/android/car/CarSensorService.java
index ffb13ca..171a8c4 100644
--- a/service/src/com/android/car/CarSensorService.java
+++ b/service/src/com/android/car/CarSensorService.java
@@ -17,6 +17,7 @@
package com.android.car;
import android.car.Car;
+import android.car.hardware.CarSensorConfig;
import android.car.hardware.CarSensorEvent;
import android.car.hardware.CarSensorManager;
import android.car.hardware.ICarSensor;
@@ -420,6 +421,7 @@
switch (sensorType) {
case CarSensorManager.SENSOR_TYPE_CAR_SPEED:
case CarSensorManager.SENSOR_TYPE_RPM:
+ case CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE:
return true;
case CarSensorManager.SENSOR_TYPE_ODOMETER:
case CarSensorManager.SENSOR_TYPE_FUEL_LEVEL:
@@ -454,6 +456,7 @@
String permission = null;
switch (sensorType) {
case CarSensorManager.SENSOR_TYPE_CAR_SPEED:
+ case CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE:
permission = Car.PERMISSION_SPEED;
break;
case CarSensorManager.SENSOR_TYPE_ODOMETER:
@@ -462,6 +465,10 @@
case CarSensorManager.SENSOR_TYPE_FUEL_LEVEL:
permission = Car.PERMISSION_FUEL;
break;
+ case CarSensorManager.SENSOR_TYPE_ABS_ACTIVE:
+ case CarSensorManager.SENSOR_TYPE_TRACTION_CONTROL_ACTIVE:
+ permission = Car.PERMISSION_VEHICLE_DYNAMICS_STATE;
+ break;
default:
break;
}
@@ -559,6 +566,22 @@
}
}
+ @Override
+ public CarSensorConfig getSensorConfig(int sensorType) {
+ if (Binder.getCallingUid() != Process.myUid()) {
+ switch (getSensorPermission(sensorType)) {
+ case PackageManager.PERMISSION_DENIED:
+ throw new SecurityException("client does not have permission:"
+ + getPermissionName(sensorType)
+ + " pid:" + Binder.getCallingPid()
+ + " uid:" + Binder.getCallingUid());
+ case PackageManager.PERMISSION_GRANTED:
+ break;
+ }
+ }
+ return(mSensorHal.getSensorConfig(sensorType));
+ }
+
private void stopSensor(SensorRecord record, int sensorType) {
if (Log.isLoggable(CarLog.TAG_SENSOR, Log.DEBUG)) {
Log.d(CarLog.TAG_SENSOR, "stopSensor " + sensorType);
diff --git a/service/src/com/android/car/CarService.java b/service/src/com/android/car/CarService.java
index 298290b..87e891e 100644
--- a/service/src/com/android/car/CarService.java
+++ b/service/src/com/android/car/CarService.java
@@ -44,11 +44,6 @@
private static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
- private static final String IVHAL_20 =
- android.hardware.automotive.vehicle.V2_0.IVehicle.kInterfaceName;
- private static final String IVHAL_21 =
- android.hardware.automotive.vehicle.V2_1.IVehicle.kInterfaceName;
-
private CanBusErrorNotifier mCanBusErrorNotifier;
private ICarImpl mICarImpl;
private IVehicle mVehicle;
@@ -77,7 +72,7 @@
public void onCreate() {
Log.i(CarLog.TAG_SERVICE, "Service onCreate");
mCanBusErrorNotifier = new CanBusErrorNotifier(this /* context */);
- mVehicle = getVehicle(null /* Any Vehicle HAL interface name */);
+ mVehicle = getVehicle();
if (mVehicle == null) {
throw new IllegalStateException("Vehicle HAL service is not available.");
@@ -152,8 +147,8 @@
}
@Nullable
- private IVehicle getVehicleWithTimeout(long waitMilliseconds, @Nullable String interfaceName) {
- IVehicle vehicle = getVehicle(interfaceName);
+ private IVehicle getVehicleWithTimeout(long waitMilliseconds) {
+ IVehicle vehicle = getVehicle();
long start = elapsedRealtime();
while (vehicle == null && (start + waitMilliseconds) > elapsedRealtime()) {
try {
@@ -162,7 +157,7 @@
throw new RuntimeException("Sleep was interrupted", e);
}
- vehicle = getVehicle(interfaceName);
+ vehicle = getVehicle();
}
if (vehicle != null) {
@@ -173,20 +168,9 @@
}
@Nullable
- private static IVehicle getVehicle(@Nullable String interfaceName) {
+ private static IVehicle getVehicle() {
try {
- boolean anyVersion = interfaceName == null || interfaceName.isEmpty();
- IVehicle vehicle = null;
- if (anyVersion || IVHAL_21.equals(interfaceName)) {
- vehicle = android.hardware.automotive.vehicle.V2_1.IVehicle
- .getService();
- }
-
- if (vehicle == null && (anyVersion || IVHAL_20.equals(interfaceName))) {
- vehicle = android.hardware.automotive.vehicle.V2_0.IVehicle
- .getService();
- }
- return vehicle;
+ return android.hardware.automotive.vehicle.V2_0.IVehicle.getService();
} catch (RemoteException e) {
Log.e(CarLog.TAG_SERVICE, "Failed to get IVehicle service", e);
} catch (NoSuchElementException e) {
@@ -213,8 +197,7 @@
Log.i(CarLog.TAG_SERVICE, "Trying to reconnect to Vehicle HAL: " +
mVehicleInterfaceName);
- mVehicle = getVehicleWithTimeout(WAIT_FOR_VEHICLE_HAL_TIMEOUT_MS,
- mVehicleInterfaceName);
+ mVehicle = getVehicleWithTimeout(WAIT_FOR_VEHICLE_HAL_TIMEOUT_MS);
if (mVehicle == null) {
throw new IllegalStateException("Failed to reconnect to Vehicle HAL");
}
diff --git a/service/src/com/android/car/CarVolumeControllerFactory.java b/service/src/com/android/car/CarVolumeControllerFactory.java
index 4e9bd4f..c83ef93 100644
--- a/service/src/com/android/car/CarVolumeControllerFactory.java
+++ b/service/src/com/android/car/CarVolumeControllerFactory.java
@@ -17,6 +17,7 @@
package com.android.car;
import android.content.Context;
+import android.hardware.automotive.vehicle.V2_0.VehicleAudioContextFlag;
import android.media.AudioManager;
import android.media.IAudioService;
import android.media.IVolumeController;
@@ -254,7 +255,8 @@
AudioHalService.carContextToCarUsage(carContext));
return physicalStream;
} else {
- return carContext;
+ return carContext == VehicleAudioContextFlag.UNKNOWN_FLAG ?
+ mCurrentContext : carContext;
}
}
@@ -533,7 +535,7 @@
synchronized (this) {
int flag = getVolumeUpdateFlag(true);
if (DBG) {
- Log.d(TAG, "onVolumeChange carStream:" + carStream + " volume: " + volume
+ Log.d(TAG, "onVolumeChange carStream: " + carStream + " volume: " + volume
+ " volumeState: " + volumeState
+ " suppressUI? " + mShouldSuppress
+ " stream: " + mSuppressUiForVolume[0]
@@ -543,6 +545,13 @@
if (mMasterVolumeOnly) { //for master volume only H/W, always assume current stream
carStream = currentCarStream;
}
+
+ // Map the UNKNOWN context to the current context.
+ if (mSupportedAudioContext != 0
+ && carStream == VehicleAudioContextFlag.UNKNOWN_FLAG) {
+ carStream = mCurrentContext;
+ }
+
if (currentCarStream == carStream) {
mCurrentCarContextVolume.put(mCurrentContext, volume);
writeVolumeToSettings(mCurrentContext, volume);
@@ -552,6 +561,7 @@
} else {
// Hal is telling us a car stream volume has changed, but it is not the current
// stream.
+ // TODO: b/63778359
Log.w(TAG, "Car stream" + carStream
+ " volume changed, but it is not current stream, ignored.");
}
diff --git a/service/src/com/android/car/DayNightModePolicy.java b/service/src/com/android/car/DayNightModePolicy.java
index dae9a14..48072eb 100644
--- a/service/src/com/android/car/DayNightModePolicy.java
+++ b/service/src/com/android/car/DayNightModePolicy.java
@@ -86,7 +86,7 @@
private static CarSensorEvent createEvent(boolean isNight, long timestamp) {
CarSensorEvent event = new CarSensorEvent(CarSensorManager.SENSOR_TYPE_NIGHT,
- timestamp, 0, 1);
+ timestamp, 0, 1, 0);
event.intValues[0] = isNight ? 1 : 0;
return event;
}
diff --git a/service/src/com/android/car/DrivingStatePolicy.java b/service/src/com/android/car/DrivingStatePolicy.java
index 167bb93..37bbbc0 100644
--- a/service/src/com/android/car/DrivingStatePolicy.java
+++ b/service/src/com/android/car/DrivingStatePolicy.java
@@ -167,7 +167,7 @@
if (lastGear != null && isGearInParkingOrNeutral(lastGear)) {
drivingState = CarSensorEvent.DRIVE_STATUS_UNRESTRICTED;
}
- } else { // parking break not applied or not available
+ } else { // parking brake not applied or not available
if (lastGear != null && isGearInParking(lastGear)) { // gear in P
drivingState = CarSensorEvent.DRIVE_STATUS_UNRESTRICTED;
}
@@ -204,7 +204,8 @@
CarSensorManager.SENSOR_TYPE_DRIVING_STATUS,
timestamp,
0 /* float values */,
- 1 /* int values */);
+ 1 /* int values */,
+ 0 /* long values */);
event.intValues[0] = drivingState;
return event;
}
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index f23f10c..bd42454 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -24,13 +24,13 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.automotive.vehicle.V2_0.IVehicle;
-import android.hardware.automotive.vehicle.V2_0.VehicleAreaDoor;
-import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
import android.os.Binder;
import android.os.IBinder;
import android.os.Process;
+import android.os.Trace;
import android.util.Log;
-
+import android.util.Slog;
+import android.util.TimingsTraceLog;
import com.android.car.cluster.InstrumentClusterService;
import com.android.car.hal.VehicleHal;
import com.android.car.internal.FeatureConfiguration;
@@ -38,7 +38,6 @@
import com.android.car.pm.CarPackageManagerService;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.car.ICarServiceHelper;
-
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -72,7 +71,6 @@
private final CarVendorExtensionService mCarVendorExtensionService;
private final CarBluetoothService mCarBluetoothService;
private final PerUserCarServiceHelper mPerUserCarServiceHelper;
- @FutureFeature
private CarDiagnosticService mCarDiagnosticService;
@FutureFeature
private VmsSubscriberService mVmsSubscriberService;
@@ -81,6 +79,11 @@
private final CarServiceBase[] mAllServices;
+ private static final String TAG = "ICarImpl";
+ private static final String VHAL_TIMING_TAG = "VehicleHalTiming";
+ private static final TimingsTraceLog mBootTiming = new TimingsTraceLog(VHAL_TIMING_TAG,
+ Trace.TRACE_TAG_HAL);
+
/** Test only service. Populate it only when necessary. */
@GuardedBy("this")
private CarTestService mCarTestService;
@@ -155,10 +158,14 @@
}
public void init() {
+ traceBegin("VehicleHal.init");
mHal.init();
+ traceEnd();
+ traceBegin("CarService.initAllServices");
for (CarServiceBase service : mAllServices) {
service.init();
}
+ traceEnd();
}
public void release() {
@@ -217,6 +224,9 @@
IInstrumentClusterNavigation navService =
mInstrumentClusterService.getNavigationService();
return navService == null ? null : navService.asBinder();
+ case Car.CAR_INSTRUMENT_CLUSTER_SERVICE:
+ assertClusterManagerPermission(mContext);
+ return mInstrumentClusterService.getManagerService();
case Car.PROJECTION_SERVICE:
assertProjectionPermission(mContext);
return mCarProjectionService;
@@ -238,6 +248,8 @@
return mCarTestService;
}
}
+ case Car.BLUETOOTH_SERVICE:
+ return mCarBluetoothService;
default:
Log.w(CarLog.TAG_SERVICE, "getCarService for unknown service:" + serviceName);
return null;
@@ -274,6 +286,10 @@
assertPermission(context, Car.PERMISSION_CAR_NAVIGATION_MANAGER);
}
+ public static void assertClusterManagerPermission(Context context) {
+ assertPermission(context, Car.PERMISSION_CAR_INSTRUMENT_CLUSTER_CONTROL);
+ }
+
public static void assertHvacPermission(Context context) {
assertPermission(context, Car.PERMISSION_CAR_HVAC);
}
@@ -290,10 +306,9 @@
assertPermission(context, Car.PERMISSION_VENDOR_EXTENSION);
}
- @FutureFeature
public static void assertAnyDiagnosticPermission(Context context) {
assertAnyPermission(context,
- Car.PERMISSION_CAR_DIAGNOSTIC_READ,
+ Car.PERMISSION_CAR_DIAGNOSTIC_READ_ALL,
Car.PERMISSION_CAR_DIAGNOSTIC_CLEAR);
}
@@ -341,6 +356,15 @@
new CarShellCommand().exec(args, writer);
}
+ private static void traceBegin(String name) {
+ Slog.i(TAG, name);
+ mBootTiming.traceBegin(name);
+ }
+
+ private static void traceEnd() {
+ mBootTiming.traceEnd();
+ }
+
private class CarShellCommand {
private static final String COMMAND_HELP = "-h";
private static final String COMMAND_DAY_NIGHT_MODE = "day-night-mode";
diff --git a/service/src/com/android/car/VmsLayersAvailability.java b/service/src/com/android/car/VmsLayersAvailability.java
index d6e89f2..93b917c 100644
--- a/service/src/com/android/car/VmsLayersAvailability.java
+++ b/service/src/com/android/car/VmsLayersAvailability.java
@@ -17,11 +17,13 @@
package com.android.car;
import android.car.annotation.FutureFeature;
+import android.car.vms.VmsAssociatedLayer;
import android.car.vms.VmsLayer;
import android.car.vms.VmsLayerDependency;
import android.car.vms.VmsLayersOffering;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -34,7 +36,7 @@
/**
* Manages VMS availability for layers.
- *
+ * <p>
* Each VMS publisher sets its layers offering which are a list of layers the publisher claims
* it might publish. VmsLayersAvailability calculates from all the offering what are the
* available layers.
@@ -49,13 +51,13 @@
private final Object mLock = new Object();
@GuardedBy("mLock")
private final Map<VmsLayer, Set<Set<VmsLayer>>> mPotentialLayersAndDependencies =
- new HashMap<>();
+ new HashMap<>();
@GuardedBy("mLock")
- private final Set<VmsLayer> mCyclicAvoidanceSet = new HashSet<>();
+ private Set<VmsAssociatedLayer> mAvailableAssociatedLayers = Collections.EMPTY_SET;
@GuardedBy("mLock")
- private Set<VmsLayer> mAvailableLayers = Collections.EMPTY_SET;
+ private Set<VmsAssociatedLayer> mUnavailableAssociatedLayers = Collections.EMPTY_SET;
@GuardedBy("mLock")
- private Set<VmsLayer> mUnavailableLayers = Collections.EMPTY_SET;
+ private Map<VmsLayer, Set<Integer>> mPotentialLayersAndPublishers = new HashMap<>();
/**
* Setting the current layers offerings as reported by publishers.
@@ -67,8 +69,19 @@
for (VmsLayersOffering offering : publishersLayersOfferings) {
for (VmsLayerDependency dependency : offering.getDependencies()) {
VmsLayer layer = dependency.getLayer();
+
+ // Associate publishers with layers.
+ Set<Integer> curPotentialLayerAndPublishers =
+ mPotentialLayersAndPublishers.get(layer);
+ if (curPotentialLayerAndPublishers == null) {
+ curPotentialLayerAndPublishers = new HashSet<>();
+ mPotentialLayersAndPublishers.put(layer, curPotentialLayerAndPublishers);
+ }
+ curPotentialLayerAndPublishers.add(offering.getPublisherId());
+
+ // Add dependencies for availability calculation.
Set<Set<VmsLayer>> curDependencies =
- mPotentialLayersAndDependencies.get(layer);
+ mPotentialLayersAndDependencies.get(layer);
if (curDependencies == null) {
curDependencies = new HashSet<>();
mPotentialLayersAndDependencies.put(layer, curDependencies);
@@ -83,9 +96,9 @@
/**
* Returns a collection of all the layers which may be published.
*/
- public Set<VmsLayer> getAvailableLayers() {
+ public Set<VmsAssociatedLayer> getAvailableLayers() {
synchronized (mLock) {
- return mAvailableLayers;
+ return mAvailableAssociatedLayers;
}
}
@@ -93,80 +106,95 @@
* Returns a collection of all the layers which publishers could have published if the
* dependencies were satisfied.
*/
- public Set<VmsLayer> getUnavailableLayers() {
+ public Set<VmsAssociatedLayer> getUnavailableLayers() {
synchronized (mLock) {
- return mUnavailableLayers;
+ return mUnavailableAssociatedLayers;
}
}
private void reset() {
synchronized (mLock) {
- mCyclicAvoidanceSet.clear();
mPotentialLayersAndDependencies.clear();
- mAvailableLayers = Collections.EMPTY_SET;
- mUnavailableLayers = Collections.EMPTY_SET;
+ mPotentialLayersAndPublishers.clear();
+ mAvailableAssociatedLayers = Collections.EMPTY_SET;
+ mUnavailableAssociatedLayers = Collections.EMPTY_SET;
}
}
private void calculateLayers() {
synchronized (mLock) {
- final Set<VmsLayer> availableLayers = new HashSet<>();
+ Set<VmsLayer> availableLayersSet = new HashSet<>();
+ Set<VmsLayer> cyclicAvoidanceAuxiliarySet = new HashSet<>();
- availableLayers.addAll(
- mPotentialLayersAndDependencies.keySet()
- .stream()
- .filter(layer -> isLayerSupportedLocked(layer, availableLayers))
- .collect(Collectors.toSet()));
+ for (VmsLayer layer : mPotentialLayersAndDependencies.keySet()) {
+ addLayerToAvailabilityCalculationLocked(layer,
+ availableLayersSet,
+ cyclicAvoidanceAuxiliarySet);
+ }
- mAvailableLayers = Collections.unmodifiableSet(availableLayers);
- mUnavailableLayers = Collections.unmodifiableSet(
- mPotentialLayersAndDependencies.keySet()
- .stream()
- .filter(layer -> !availableLayers.contains(layer))
- .collect(Collectors.toSet()));
+ mAvailableAssociatedLayers = Collections.unmodifiableSet(
+ availableLayersSet
+ .stream()
+ .map(l -> new VmsAssociatedLayer(l, mPotentialLayersAndPublishers.get(l)))
+ .collect(Collectors.toSet()));
+
+ mUnavailableAssociatedLayers = Collections.unmodifiableSet(
+ mPotentialLayersAndDependencies.keySet()
+ .stream()
+ .filter(l -> !availableLayersSet.contains(l))
+ .map(l -> new VmsAssociatedLayer(l, mPotentialLayersAndPublishers.get(l)))
+ .collect(Collectors.toSet()));
}
}
- private boolean isLayerSupportedLocked(VmsLayer layer, Set<VmsLayer> currentAvailableLayers) {
+ private void addLayerToAvailabilityCalculationLocked(VmsLayer layer,
+ Set<VmsLayer> currentAvailableLayers,
+ Set<VmsLayer> cyclicAvoidanceSet) {
if (DBG) {
- Log.d(TAG, "isLayerSupported: checking layer: " + layer);
+ Log.d(TAG, "addLayerToAvailabilityCalculationLocked: checking layer: " + layer);
}
// If we already know that this layer is supported then we are done.
if (currentAvailableLayers.contains(layer)) {
- return true;
+ return;
}
// If there is no offering for this layer we're done.
if (!mPotentialLayersAndDependencies.containsKey(layer)) {
- return false;
+ return;
}
// Avoid cyclic dependency.
- if (mCyclicAvoidanceSet.contains(layer)) {
- Log.e(TAG, "Detected a cyclic dependency: " + mCyclicAvoidanceSet + " -> " + layer);
- return false;
+ if (cyclicAvoidanceSet.contains(layer)) {
+ Log.e(TAG, "Detected a cyclic dependency: " + cyclicAvoidanceSet + " -> " + layer);
+ return;
}
+ // A layer may have multiple dependency sets. The layer is available if any dependency
+ // set is satisfied
for (Set<VmsLayer> dependencies : mPotentialLayersAndDependencies.get(layer)) {
// If layer does not have any dependencies then add to supported.
if (dependencies == null || dependencies.isEmpty()) {
currentAvailableLayers.add(layer);
- return true;
+ return;
}
// Add the layer to cyclic avoidance set
- mCyclicAvoidanceSet.add(layer);
+ cyclicAvoidanceSet.add(layer);
boolean isSupported = true;
for (VmsLayer dependency : dependencies) {
- if (!isLayerSupportedLocked(dependency, currentAvailableLayers)) {
+ addLayerToAvailabilityCalculationLocked(dependency,
+ currentAvailableLayers,
+ cyclicAvoidanceSet);
+
+ if (!currentAvailableLayers.contains(dependency)) {
isSupported = false;
break;
}
}
- mCyclicAvoidanceSet.remove(layer);
+ cyclicAvoidanceSet.remove(layer);
if (isSupported) {
currentAvailableLayers.add(layer);
- return true;
+ return;
}
}
- return false;
+ return;
}
}
diff --git a/service/src/com/android/car/VmsPublisherService.java b/service/src/com/android/car/VmsPublisherService.java
index 37d265a..8a293e5 100644
--- a/service/src/com/android/car/VmsPublisherService.java
+++ b/service/src/com/android/car/VmsPublisherService.java
@@ -109,14 +109,15 @@
// Implements IVmsPublisherService interface.
@Override
- public void publish(IBinder token, VmsLayer layer, byte[] payload) {
+ public void publish(IBinder token, VmsLayer layer, int publisherId, byte[] payload) {
if (DBG) {
Log.d(TAG, "Publishing for layer: " + layer);
}
ICarImpl.assertVmsPublisherPermission(mContext);
// Send the message to application listeners.
- Set<IVmsSubscriberClient> listeners = mHal.getListeners(layer);
+ Set<IVmsSubscriberClient> listeners =
+ mHal.getSubscribersForLayerFromPublisher(layer, publisherId);
if (DBG) {
Log.d(TAG, "Number of subscribed apps: " + listeners.size());
@@ -145,9 +146,9 @@
}
@Override
- public int getPublisherStaticId(byte[] publisherInfo) {
+ public int getPublisherId(byte[] publisherInfo) {
ICarImpl.assertVmsPublisherPermission(mContext);
- return mHal.getPublisherStaticId(publisherInfo);
+ return mHal.getPublisherId(publisherInfo);
}
// Implements VmsHalListener interface
diff --git a/service/src/com/android/car/VmsRouting.java b/service/src/com/android/car/VmsRouting.java
index 2829cc0..2e66781 100644
--- a/service/src/com/android/car/VmsRouting.java
+++ b/service/src/com/android/car/VmsRouting.java
@@ -18,15 +18,20 @@
import android.car.annotation.FutureFeature;
import android.car.vms.IVmsSubscriberClient;
+import android.car.vms.VmsAssociatedLayer;
import android.car.vms.VmsLayer;
+import android.car.vms.VmsOperationRecorder;
import android.car.vms.VmsSubscriptionState;
+
import com.android.internal.annotations.GuardedBy;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* Manages all the VMS subscriptions:
@@ -37,53 +42,93 @@
@FutureFeature
public class VmsRouting {
private final Object mLock = new Object();
- // A map of Layer + Version to listeners.
+ // A map of Layer + Version to subscribers.
@GuardedBy("mLock")
- private Map<VmsLayer, Set<IVmsSubscriberClient>> mLayerSubscriptions =
- new HashMap<>();
- // A set of listeners that are interested in any layer + version.
+ private Map<VmsLayer, Set<IVmsSubscriberClient>> mLayerSubscriptions = new HashMap<>();
+
@GuardedBy("mLock")
- private Set<IVmsSubscriberClient> mPromiscuousSubscribers =
- new HashSet<>();
+ private Map<VmsLayer, Map<Integer, Set<IVmsSubscriberClient>>> mLayerSubscriptionsToPublishers =
+ new HashMap<>();
+ // A set of subscribers that are interested in any layer + version.
+ @GuardedBy("mLock")
+ private Set<IVmsSubscriberClient> mPromiscuousSubscribers = new HashSet<>();
+
// A set of all the layers + versions the HAL is subscribed to.
@GuardedBy("mLock")
private Set<VmsLayer> mHalSubscriptions = new HashSet<>();
+
+ @GuardedBy("mLock")
+ private Map<VmsLayer, Set<Integer>> mHalSubscriptionsToPublishers = new HashMap<>();
// A sequence number that is increased every time the subscription state is modified. Note that
// modifying the list of promiscuous subscribers does not affect the subscription state.
@GuardedBy("mLock")
private int mSequenceNumber = 0;
/**
- * Add a listener subscription to a data messages from layer + version.
+ * Add a subscriber subscription to data messages from a VMS layer.
*
- * @param listener a VMS subscriber.
- * @param layer the layer subscribing to.
+ * @param subscriber a VMS subscriber.
+ * @param layer the layer subscribing to.
*/
- public void addSubscription(IVmsSubscriberClient listener, VmsLayer layer) {
+ public void addSubscription(IVmsSubscriberClient subscriber, VmsLayer layer) {
//TODO(b/36902947): revise if need to sync, and return value.
synchronized (mLock) {
++mSequenceNumber;
- // Get or create the list of listeners for layer and version.
- Set<IVmsSubscriberClient> listeners = mLayerSubscriptions.get(layer);
+ // Get or create the list of subscribers for layer and version.
+ Set<IVmsSubscriberClient> subscribers = mLayerSubscriptions.get(layer);
- if (listeners == null) {
- listeners = new HashSet<>();
- mLayerSubscriptions.put(layer, listeners);
+ if (subscribers == null) {
+ subscribers = new HashSet<>();
+ mLayerSubscriptions.put(layer, subscribers);
}
- // Add the listener to the list.
- listeners.add(listener);
+ // Add the subscriber to the list.
+ subscribers.add(subscriber);
+ VmsOperationRecorder.get().addSubscription(mSequenceNumber, layer);
}
}
/**
- * Add a listener subscription to all data messages.
+ * Add a subscriber subscription to all data messages.
*
- * @param listener a VMS subscriber.
+ * @param subscriber a VMS subscriber.
*/
- public void addSubscription(IVmsSubscriberClient listener) {
+ public void addSubscription(IVmsSubscriberClient subscriber) {
synchronized (mLock) {
++mSequenceNumber;
- mPromiscuousSubscribers.add(listener);
+ mPromiscuousSubscribers.add(subscriber);
+ VmsOperationRecorder.get().addPromiscuousSubscription(mSequenceNumber);
+ }
+ }
+
+ /**
+ * Add a subscriber subscription to data messages from a VMS layer from a specific publisher.
+ *
+ * @param subscriber a VMS subscriber.
+ * @param layer the layer to subscribing to.
+ * @param publisherId the publisher ID.
+ */
+ public void addSubscription(IVmsSubscriberClient subscriber, VmsLayer layer, int publisherId) {
+ synchronized (mLock) {
+ ++mSequenceNumber;
+
+ Map<Integer, Set<IVmsSubscriberClient>> publisherIdsToSubscribersForLayer =
+ mLayerSubscriptionsToPublishers.get(layer);
+
+ if (publisherIdsToSubscribersForLayer == null) {
+ publisherIdsToSubscribersForLayer = new HashMap<>();
+ mLayerSubscriptionsToPublishers.put(layer, publisherIdsToSubscribersForLayer);
+ }
+
+ Set<IVmsSubscriberClient> subscribersForPublisher =
+ publisherIdsToSubscribersForLayer.get(publisherId);
+
+ if (subscribersForPublisher == null) {
+ subscribersForPublisher = new HashSet<>();
+ publisherIdsToSubscribersForLayer.put(publisherId, subscribersForPublisher);
+ }
+
+ // Add the subscriber to the list.
+ subscribersForPublisher.add(subscriber);
}
}
@@ -91,132 +136,217 @@
* Remove a subscription for a layer + version and make sure to remove the key if there are no
* more subscribers.
*
- * @param listener to remove.
- * @param layer of the subscription.
+ * @param subscriber to remove.
+ * @param layer of the subscription.
*/
- public void removeSubscription(IVmsSubscriberClient listener, VmsLayer layer) {
+ public void removeSubscription(IVmsSubscriberClient subscriber, VmsLayer layer) {
synchronized (mLock) {
++mSequenceNumber;
- Set<IVmsSubscriberClient> listeners = mLayerSubscriptions.get(layer);
+ Set<IVmsSubscriberClient> subscribers = mLayerSubscriptions.get(layer);
- // If there are no listeners we are done.
- if (listeners == null) {
+ // If there are no subscribers we are done.
+ if (subscribers == null) {
return;
}
- listeners.remove(listener);
+ subscribers.remove(subscriber);
+ VmsOperationRecorder.get().removeSubscription(mSequenceNumber, layer);
- // If there are no more listeners then remove the list.
- if (listeners.isEmpty()) {
+ // If there are no more subscribers then remove the list.
+ if (subscribers.isEmpty()) {
mLayerSubscriptions.remove(layer);
}
}
}
/**
- * Remove a listener subscription to all data messages.
+ * Remove a subscriber subscription to all data messages.
*
- * @param listener a VMS subscriber.
+ * @param subscriber a VMS subscriber.
*/
- public void removeSubscription(IVmsSubscriberClient listener) {
+ public void removeSubscription(IVmsSubscriberClient subscriber) {
synchronized (mLock) {
++mSequenceNumber;
- mPromiscuousSubscribers.remove(listener);
+ mPromiscuousSubscribers.remove(subscriber);
+ VmsOperationRecorder.get().removePromiscuousSubscription(mSequenceNumber);
+ }
+ }
+
+ /**
+ * Remove a subscription to data messages from a VMS layer from a specific publisher.
+ *
+ * @param subscriber a VMS subscriber.
+ * @param layer the layer to unsubscribing from.
+ * @param publisherId the publisher ID.
+ */
+ public void removeSubscription(IVmsSubscriberClient subscriber,
+ VmsLayer layer,
+ int publisherId) {
+ synchronized (mLock) {
+ ++mSequenceNumber;
+
+ Map<Integer, Set<IVmsSubscriberClient>> subscribersToPublishers =
+ mLayerSubscriptionsToPublishers.get(layer);
+
+ if (subscribersToPublishers == null) {
+ return;
+ }
+
+ Set<IVmsSubscriberClient> subscribers = subscribersToPublishers.get(publisherId);
+
+ if (subscribers == null) {
+ return;
+ }
+ subscribers.remove(subscriber);
+
+ if (subscribers.isEmpty()) {
+ subscribersToPublishers.remove(publisherId);
+ }
+
+ if (subscribersToPublishers.isEmpty()) {
+ mLayerSubscriptionsToPublishers.remove(layer);
+ }
}
}
/**
* Remove a subscriber from all routes (optional operation).
*
- * @param listener a VMS subscriber.
+ * @param subscriber a VMS subscriber.
*/
- public void removeDeadListener(IVmsSubscriberClient listener) {
+ public void removeDeadSubscriber(IVmsSubscriberClient subscriber) {
synchronized (mLock) {
- // Remove the listener from all the routes.
+ // Remove the subscriber from all the routes.
for (VmsLayer layer : mLayerSubscriptions.keySet()) {
- removeSubscription(listener, layer);
+ removeSubscription(subscriber, layer);
}
- // Remove the listener from the loggers.
- removeSubscription(listener);
+ // Remove the subscriber from the loggers.
+ removeSubscription(subscriber);
}
}
/**
- * Returns a list with all the listeners for a layer and version. This include the subscribers
- * which explicitly subscribed to this layer and version and the promiscuous subscribers.
+ * Returns a list of all the subscribers for a layer from a publisher. This includes
+ * subscribers that subscribed to this layer from all publishers, subscribed to this layer
+ * from a specific publisher, and the promiscuous subscribers.
*
- * @param layer to get listeners to.
- * @return a list of the listeners.
+ * @param layer The layer of the message.
+ * @param publisherId the ID of the client that published the message to be routed.
+ * @return a list of the subscribers.
*/
- public Set<IVmsSubscriberClient> getListeners(VmsLayer layer) {
- Set<IVmsSubscriberClient> listeners = new HashSet<>();
+ public Set<IVmsSubscriberClient> getSubscribersForLayerFromPublisher(VmsLayer layer,
+ int publisherId) {
+ Set<IVmsSubscriberClient> subscribers = new HashSet<>();
synchronized (mLock) {
- // Add the subscribers which explicitly subscribed to this layer and version
+ // Add the subscribers which explicitly subscribed to this layer
if (mLayerSubscriptions.containsKey(layer)) {
- listeners.addAll(mLayerSubscriptions.get(layer));
+ subscribers.addAll(mLayerSubscriptions.get(layer));
}
+
+ // Add the subscribers which explicitly subscribed to this layer and publisher
+ if (mLayerSubscriptionsToPublishers.containsKey(layer)) {
+ if (mLayerSubscriptionsToPublishers.get(layer).containsKey(publisherId)) {
+ subscribers.addAll(mLayerSubscriptionsToPublishers.get(layer).get(publisherId));
+ }
+ }
+
// Add the promiscuous subscribers.
- listeners.addAll(mPromiscuousSubscribers);
+ subscribers.addAll(mPromiscuousSubscribers);
}
- return listeners;
+ return subscribers;
}
/**
- * Returns a list with all the listeners.
+ * Returns a list with all the subscribers.
*/
- public Set<IVmsSubscriberClient> getAllListeners() {
- Set<IVmsSubscriberClient> listeners = new HashSet<>();
+ public Set<IVmsSubscriberClient> getAllSubscribers() {
+ Set<IVmsSubscriberClient> subscribers = new HashSet<>();
synchronized (mLock) {
for (VmsLayer layer : mLayerSubscriptions.keySet()) {
- listeners.addAll(mLayerSubscriptions.get(layer));
+ subscribers.addAll(mLayerSubscriptions.get(layer));
}
// Add the promiscuous subscribers.
- listeners.addAll(mPromiscuousSubscribers);
+ subscribers.addAll(mPromiscuousSubscribers);
}
- return listeners;
+ return subscribers;
}
/**
- * Checks if a listener is subscribed to any messages.
- * @param listener that may have subscription.
- * @return true if the listener uis subscribed to messages.
+ * Checks if a subscriber is subscribed to any messages.
+ *
+ * @param subscriber that may have subscription.
+ * @return true if the subscriber uis subscribed to messages.
*/
- public boolean containsListener(IVmsSubscriberClient listener) {
+ public boolean containsSubscriber(IVmsSubscriberClient subscriber) {
synchronized (mLock) {
- // Check if listener is subscribed to a layer.
- for (Set<IVmsSubscriberClient> layerListeners: mLayerSubscriptions.values()) {
- if (layerListeners.contains(listener)) {
+ // Check if subscriber is subscribed to a layer.
+ for (Set<IVmsSubscriberClient> layerSubscribers : mLayerSubscriptions.values()) {
+ if (layerSubscribers.contains(subscriber)) {
return true;
}
}
- // Check is listener is subscribed to all data messages.
- return mPromiscuousSubscribers.contains(listener);
+ // Check is subscriber is subscribed to all data messages.
+ return mPromiscuousSubscribers.contains(subscriber);
}
}
/**
* Add a layer and version to the HAL subscriptions.
+ *
* @param layer the HAL subscribes to.
*/
public void addHalSubscription(VmsLayer layer) {
synchronized (mLock) {
++mSequenceNumber;
mHalSubscriptions.add(layer);
+ VmsOperationRecorder.get().addHalSubscription(mSequenceNumber, layer);
+ }
+ }
+
+ public void addHalSubscriptionToPublisher(VmsLayer layer, int publisherId) {
+ synchronized (mLock) {
+ ++mSequenceNumber;
+
+ Set<Integer> publisherIdsForLayer = mHalSubscriptionsToPublishers.get(layer);
+ if (publisherIdsForLayer == null) {
+ publisherIdsForLayer = new HashSet<>();
+ mHalSubscriptionsToPublishers.put(layer, publisherIdsForLayer);
+ }
+ publisherIdsForLayer.add(publisherId);
}
}
/**
* remove a layer and version to the HAL subscriptions.
+ *
* @param layer the HAL unsubscribes from.
*/
public void removeHalSubscription(VmsLayer layer) {
synchronized (mLock) {
++mSequenceNumber;
mHalSubscriptions.remove(layer);
+ VmsOperationRecorder.get().removeHalSubscription(mSequenceNumber, layer);
+ }
+ }
+
+ public void removeHalSubscriptionToPublisher(VmsLayer layer, int publisherId) {
+ synchronized (mLock) {
+ ++mSequenceNumber;
+
+ Set<Integer> publisherIdsForLayer = mHalSubscriptionsToPublishers.get(layer);
+ if (publisherIdsForLayer == null) {
+ return;
+ }
+ publisherIdsForLayer.remove(publisherId);
+
+ if (publisherIdsForLayer.isEmpty()) {
+ mHalSubscriptionsToPublishers.remove(layer);
+ }
}
}
/**
* checks if the HAL is subscribed to a layer.
+ *
* @param layer
* @return true if the HAL is subscribed to layer.
*/
@@ -228,6 +358,7 @@
/**
* checks if there are subscribers to a layer.
+ *
* @param layer
* @return true if there are subscribers to layer.
*/
@@ -238,14 +369,46 @@
}
/**
+ * returns true if there is already a subscription for the layer from publisherId.
+ *
+ * @param layer
+ * @param publisherId
+ * @return
+ */
+ public boolean hasLayerFromPublisherSubscriptions(VmsLayer layer, int publisherId) {
+ synchronized (mLock) {
+ boolean hasClientSubscription =
+ mLayerSubscriptionsToPublishers.containsKey(layer) &&
+ mLayerSubscriptionsToPublishers.get(layer).containsKey(publisherId);
+
+ boolean hasHalSubscription = mHalSubscriptionsToPublishers.containsKey(layer) &&
+ mHalSubscriptionsToPublishers.get(layer).contains(publisherId);
+
+ return hasClientSubscription || hasHalSubscription;
+ }
+ }
+
+ /**
* @return a Set of layers and versions which VMS clients are subscribed to.
*/
public VmsSubscriptionState getSubscriptionState() {
synchronized (mLock) {
- List<VmsLayer> layers = new ArrayList<>();
+ Set<VmsLayer> layers = new HashSet<>();
layers.addAll(mLayerSubscriptions.keySet());
layers.addAll(mHalSubscriptions);
- return new VmsSubscriptionState(mSequenceNumber, layers);
+
+
+ Set<VmsAssociatedLayer> layersFromPublishers = new HashSet<>();
+ layersFromPublishers.addAll(mLayerSubscriptionsToPublishers.entrySet()
+ .stream()
+ .map(e -> new VmsAssociatedLayer(e.getKey(), e.getValue().keySet()))
+ .collect(Collectors.toSet()));
+ layersFromPublishers.addAll(mHalSubscriptionsToPublishers.entrySet()
+ .stream()
+ .map(e -> new VmsAssociatedLayer(e.getKey(), e.getValue()))
+ .collect(Collectors.toSet()));
+
+ return new VmsSubscriptionState(mSequenceNumber, layers, layersFromPublishers);
}
}
}
\ No newline at end of file
diff --git a/service/src/com/android/car/VmsSubscriberService.java b/service/src/com/android/car/VmsSubscriberService.java
index fc0a885..94f0844 100644
--- a/service/src/com/android/car/VmsSubscriberService.java
+++ b/service/src/com/android/car/VmsSubscriberService.java
@@ -20,6 +20,7 @@
import android.car.annotation.FutureFeature;
import android.car.vms.IVmsSubscriberClient;
import android.car.vms.IVmsSubscriberService;
+import android.car.vms.VmsAssociatedLayer;
import android.car.vms.VmsLayer;
import android.content.Context;
import android.os.IBinder;
@@ -33,6 +34,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -52,28 +54,28 @@
private final VmsHalService mHal;
@GuardedBy("mSubscriberServiceLock")
- private final VmsListenerManager mMessageReceivedManager = new VmsListenerManager();
+ private final VmsSubscribersManager mSubscribersManager = new VmsSubscribersManager();
private final Object mSubscriberServiceLock = new Object();
/**
- * Keeps track of listeners of this service.
+ * Keeps track of subscribers of this service.
*/
- class VmsListenerManager {
+ class VmsSubscribersManager {
/**
- * Allows to modify mListenerMap and mListenerDeathRecipientMap as a single unit.
+ * Allows to modify mSubscriberMap and mListenerDeathRecipientMap as a single unit.
*/
private final Object mListenerManagerLock = new Object();
@GuardedBy("mListenerManagerLock")
private final Map<IBinder, ListenerDeathRecipient> mListenerDeathRecipientMap =
new HashMap<>();
@GuardedBy("mListenerManagerLock")
- private final Map<IBinder, IVmsSubscriberClient> mListenerMap = new HashMap<>();
+ private final Map<IBinder, IVmsSubscriberClient> mSubscriberMap = new HashMap<>();
class ListenerDeathRecipient implements IBinder.DeathRecipient {
- private IBinder mListenerBinder;
+ private IBinder mSubscriberBinder;
- ListenerDeathRecipient(IBinder listenerBinder) {
- mListenerBinder = listenerBinder;
+ ListenerDeathRecipient(IBinder subscriberBinder) {
+ mSubscriberBinder = subscriberBinder;
}
/**
@@ -82,27 +84,27 @@
@Override
public void binderDied() {
if (DBG) {
- Log.d(TAG, "binderDied " + mListenerBinder);
+ Log.d(TAG, "binderDied " + mSubscriberBinder);
}
// Get the Listener from the Binder
- IVmsSubscriberClient listener = mListenerMap.get(mListenerBinder);
+ IVmsSubscriberClient subscriber = mSubscriberMap.get(mSubscriberBinder);
- // Remove the listener subscriptions.
- if (listener != null) {
- Log.d(TAG, "Removing subscriptions for dead listener: " + listener);
- mHal.removeDeadListener(listener);
+ // Remove the subscriber subscriptions.
+ if (subscriber != null) {
+ Log.d(TAG, "Removing subscriptions for dead subscriber: " + subscriber);
+ mHal.removeDeadSubscriber(subscriber);
} else {
- Log.d(TAG, "Handling dead binder with no matching listener");
+ Log.d(TAG, "Handling dead binder with no matching subscriber");
}
// Remove binder
- VmsListenerManager.this.removeListener(mListenerBinder);
+ VmsSubscribersManager.this.removeListener(mSubscriberBinder);
}
void release() {
- mListenerBinder.unlinkToDeath(this, 0);
+ mSubscriberBinder.unlinkToDeath(this, 0);
}
}
@@ -111,85 +113,86 @@
recipient.release();
}
mListenerDeathRecipientMap.clear();
- mListenerMap.clear();
+ mSubscriberMap.clear();
}
/**
- * Adds the listener and a death recipient associated to it.
+ * Adds the subscriber and a death recipient associated to it.
*
- * @param listener to be added.
- * @throws IllegalArgumentException if the listener is null.
+ * @param subscriber to be added.
+ * @throws IllegalArgumentException if the subscriber is null.
* @throws IllegalStateException if it was not possible to link a death recipient to the
- * listener.
+ * subscriber.
*/
- public void add(IVmsSubscriberClient listener) {
+ public void add(IVmsSubscriberClient subscriber) {
ICarImpl.assertPermission(mContext, PERMISSION);
- if (listener == null) {
- Log.e(TAG, "register: listener is null.");
- throw new IllegalArgumentException("listener cannot be null.");
+ if (subscriber == null) {
+ Log.e(TAG, "register: subscriber is null.");
+ throw new IllegalArgumentException("subscriber cannot be null.");
}
if (DBG) {
- Log.d(TAG, "register: " + listener);
+ Log.d(TAG, "register: " + subscriber);
}
- IBinder listenerBinder = listener.asBinder();
+ IBinder subscriberBinder = subscriber.asBinder();
synchronized (mListenerManagerLock) {
- if (mListenerMap.containsKey(listenerBinder)) {
+ if (mSubscriberMap.containsKey(subscriberBinder)) {
// Already registered, nothing to do.
return;
}
- ListenerDeathRecipient deathRecipient = new ListenerDeathRecipient(listenerBinder);
+ ListenerDeathRecipient deathRecipient =
+ new ListenerDeathRecipient(subscriberBinder);
try {
- listenerBinder.linkToDeath(deathRecipient, 0);
+ subscriberBinder.linkToDeath(deathRecipient, 0);
} catch (RemoteException e) {
Log.e(TAG, "Failed to link death for recipient. ", e);
throw new IllegalStateException(Car.CAR_NOT_CONNECTED_EXCEPTION_MSG);
}
- mListenerDeathRecipientMap.put(listenerBinder, deathRecipient);
- mListenerMap.put(listenerBinder, listener);
+ mListenerDeathRecipientMap.put(subscriberBinder, deathRecipient);
+ mSubscriberMap.put(subscriberBinder, subscriber);
}
}
/**
- * Removes the listener and associated death recipient.
+ * Removes the subscriber and associated death recipient.
*
- * @param listener to be removed.
- * @throws IllegalArgumentException if listener is null.
+ * @param subscriber to be removed.
+ * @throws IllegalArgumentException if subscriber is null.
*/
- public void remove(IVmsSubscriberClient listener) {
+ public void remove(IVmsSubscriberClient subscriber) {
if (DBG) {
Log.d(TAG, "unregisterListener");
}
ICarImpl.assertPermission(mContext, PERMISSION);
- if (listener == null) {
- Log.e(TAG, "unregister: listener is null.");
+ if (subscriber == null) {
+ Log.e(TAG, "unregister: subscriber is null.");
throw new IllegalArgumentException("Listener is null");
}
- IBinder listenerBinder = listener.asBinder();
- removeListener(listenerBinder);
+ IBinder subscriberBinder = subscriber.asBinder();
+ removeListener(subscriberBinder);
}
- // Removes the listenerBinder from the current state.
- // The function assumes that binder will exist both in listeners and death recipients list.
- private void removeListener(IBinder listenerBinder) {
+ // Removes the subscriberBinder from the current state.
+ // The function assumes that binder will exist both in subscriber and death recipients list.
+ private void removeListener(IBinder subscriberBinder) {
synchronized (mListenerManagerLock) {
- boolean found = mListenerMap.remove(listenerBinder) != null;
+ boolean found = mSubscriberMap.remove(subscriberBinder) != null;
if (found) {
- mListenerDeathRecipientMap.get(listenerBinder).release();
- mListenerDeathRecipientMap.remove(listenerBinder);
+ mListenerDeathRecipientMap.get(subscriberBinder).release();
+ mListenerDeathRecipientMap.remove(subscriberBinder);
} else {
- Log.e(TAG, "removeListener: listener was not previously registered.");
+ Log.e(TAG, "removeListener: subscriber was not previously registered.");
}
}
}
/**
- * Returns list of listeners currently registered.
+ * Returns list of subscribers currently registered.
*
- * @return list of listeners.
+ * @return list of subscribers.
*/
public List<IVmsSubscriberClient> getListeners() {
synchronized (mListenerManagerLock) {
- return new ArrayList<>(mListenerMap.values());
+ return new ArrayList<>(mSubscriberMap.values());
}
}
}
@@ -207,7 +210,7 @@
@Override
public void release() {
- mMessageReceivedManager.release();
+ mSubscribersManager.release();
mHal.removeSubscriberListener(this);
}
@@ -217,47 +220,78 @@
// Implements IVmsService interface.
@Override
- public void addVmsSubscriberClientListener(IVmsSubscriberClient listener, VmsLayer layer) {
+ public void addVmsSubscriberToNotifications(IVmsSubscriberClient subscriber) {
synchronized (mSubscriberServiceLock) {
- // Add the listener so it can subscribe.
- mMessageReceivedManager.add(listener);
+ // Add the subscriber so it can subscribe.
+ mSubscribersManager.add(subscriber);
+ }
+ }
+
+ @Override
+ public void removeVmsSubscriberToNotifications(IVmsSubscriberClient subscriber) {
+ synchronized (mSubscriberServiceLock) {
+ if (mHal.containsSubscriber(subscriber)) {
+ throw new IllegalArgumentException("Subscriber has active subscriptions.");
+ }
+ mSubscribersManager.remove(subscriber);
+ }
+ }
+
+ @Override
+ public void addVmsSubscriber(IVmsSubscriberClient subscriber, VmsLayer layer) {
+ synchronized (mSubscriberServiceLock) {
+ // Add the subscriber so it can subscribe.
+ mSubscribersManager.add(subscriber);
// Add the subscription for the layer.
- mHal.addSubscription(listener, layer);
+ mHal.addSubscription(subscriber, layer);
}
}
@Override
- public void removeVmsSubscriberClientListener(IVmsSubscriberClient listener, VmsLayer layer) {
+ public void removeVmsSubscriber(IVmsSubscriberClient subscriber, VmsLayer layer) {
synchronized (mSubscriberServiceLock) {
// Remove the subscription.
- mHal.removeSubscription(listener, layer);
-
- // Remove the listener if it has no more subscriptions.
- if (!mHal.containsListener(listener)) {
- mMessageReceivedManager.remove(listener);
- }
+ mHal.removeSubscription(subscriber, layer);
}
}
@Override
- public void addVmsSubscriberClientPassiveListener(IVmsSubscriberClient listener) {
+ public void addVmsSubscriberToPublisher(IVmsSubscriberClient subscriber,
+ VmsLayer layer,
+ int publisherId) {
synchronized (mSubscriberServiceLock) {
- mMessageReceivedManager.add(listener);
- mHal.addSubscription(listener);
+ // Add the subscriber so it can subscribe.
+ mSubscribersManager.add(subscriber);
+
+ // Add the subscription for the layer.
+ mHal.addSubscription(subscriber, layer, publisherId);
}
}
@Override
- public void removeVmsSubscriberClientPassiveListener(IVmsSubscriberClient listener) {
+ public void removeVmsSubscriberToPublisher(IVmsSubscriberClient subscriber,
+ VmsLayer layer,
+ int publisherId) {
synchronized (mSubscriberServiceLock) {
// Remove the subscription.
- mHal.removeSubscription(listener);
+ mHal.removeSubscription(subscriber, layer, publisherId);
+ }
+ }
- // Remove the listener if it has no more subscriptions.
- if (!mHal.containsListener(listener)) {
- mMessageReceivedManager.remove(listener);
- }
+ @Override
+ public void addVmsSubscriberPassive(IVmsSubscriberClient subscriber) {
+ synchronized (mSubscriberServiceLock) {
+ mSubscribersManager.add(subscriber);
+ mHal.addSubscription(subscriber);
+ }
+ }
+
+ @Override
+ public void removeVmsSubscriberPassive(IVmsSubscriberClient subscriber) {
+ synchronized (mSubscriberServiceLock) {
+ // Remove the subscription.
+ mHal.removeSubscription(subscriber);
}
}
@@ -276,14 +310,15 @@
// Implements VmsHalSubscriberListener interface
@Override
- public void onDataMessage(VmsLayer layer, byte[] payload) {
- if(DBG) {
+ public void onDataMessage(VmsLayer layer, int publisherId, byte[] payload) {
+ if (DBG) {
Log.d(TAG, "Publishing a message for layer: " + layer);
}
- Set<IVmsSubscriberClient> listeners = mHal.getListeners(layer);
+ Set<IVmsSubscriberClient> subscribers =
+ mHal.getSubscribersForLayerFromPublisher(layer, publisherId);
- for (IVmsSubscriberClient subscriber : listeners) {
+ for (IVmsSubscriberClient subscriber : subscribers) {
try {
subscriber.onVmsMessageReceived(layer, payload);
} catch (RemoteException e) {
@@ -295,20 +330,23 @@
}
@Override
- public void onLayersAvaiabilityChange(List<VmsLayer> availableLayers) {
- if(DBG) {
+ public void onLayersAvaiabilityChange(List<VmsAssociatedLayer> availableLayers) {
+ if (DBG) {
Log.d(TAG, "Publishing layers availability change: " + availableLayers);
}
- Set<IVmsSubscriberClient> listeners = mHal.getAllListeners();
+ Set<IVmsSubscriberClient> subscribers;
+ synchronized (mSubscriberServiceLock) {
+ subscribers = new HashSet<>(mSubscribersManager.getListeners());
+ }
- for (IVmsSubscriberClient subscriber : listeners) {
+ for (IVmsSubscriberClient subscriber : subscribers) {
try {
- subscriber.onLayersAvailabilityChange(availableLayers);
+ subscriber.onLayersAvailabilityChanged(availableLayers);
} catch (RemoteException e) {
// If we could not send a record, its likely the connection snapped. Let the binder
// death handle the situation.
- Log.e(TAG, "onLayersAvailabilityChange calling failed: ", e);
+ Log.e(TAG, "onLayersAvailabilityChanged calling failed: ", e);
}
}
}
diff --git a/service/src/com/android/car/VolumeUtils.java b/service/src/com/android/car/VolumeUtils.java
index 3164aea..265615b 100644
--- a/service/src/com/android/car/VolumeUtils.java
+++ b/service/src/com/android/car/VolumeUtils.java
@@ -43,6 +43,7 @@
VehicleAudioContextFlag.NAVIGATION_FLAG,
VehicleAudioContextFlag.VOICE_COMMAND_FLAG,
VehicleAudioContextFlag.CALL_FLAG,
+ VehicleAudioContextFlag.RINGTONE_FLAG,
VehicleAudioContextFlag.ALARM_FLAG,
VehicleAudioContextFlag.NOTIFICATION_FLAG,
VehicleAudioContextFlag.UNKNOWN_FLAG,
@@ -67,6 +68,8 @@
CarSettings.Global.KEY_VOLUME_VOICE_COMMAND);
CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.CALL_FLAG,
CarSettings.Global.KEY_VOLUME_CALL);
+ CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.RINGTONE_FLAG,
+ CarSettings.Global.KEY_VOLUME_RINGTONE);
CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.ALARM_FLAG,
CarSettings.Global.KEY_VOLUME_ALARM);
CAR_AUDIO_CONTEXT_SETTINGS.put(
@@ -106,7 +109,7 @@
case AudioManager.STREAM_SYSTEM:
return VehicleAudioContextFlag.SYSTEM_SOUND_FLAG;
case AudioManager.STREAM_RING:
- return VehicleAudioContextFlag.NOTIFICATION_FLAG;
+ return VehicleAudioContextFlag.RINGTONE_FLAG;
case AudioManager.STREAM_MUSIC:
return VehicleAudioContextFlag.MUSIC_FLAG;
case AudioManager.STREAM_ALARM:
@@ -124,6 +127,8 @@
switch (carContext) {
case VehicleAudioContextFlag.CALL_FLAG:
return AudioManager.STREAM_VOICE_CALL;
+ case VehicleAudioContextFlag.RINGTONE_FLAG:
+ return AudioManager.STREAM_RING;
case VehicleAudioContextFlag.SYSTEM_SOUND_FLAG:
return AudioManager.STREAM_SYSTEM;
case VehicleAudioContextFlag.NOTIFICATION_FLAG:
diff --git a/service/src/com/android/car/cluster/InstrumentClusterService.java b/service/src/com/android/car/cluster/InstrumentClusterService.java
index 8e0507b..eef177c 100644
--- a/service/src/com/android/car/cluster/InstrumentClusterService.java
+++ b/service/src/com/android/car/cluster/InstrumentClusterService.java
@@ -15,19 +15,34 @@
*/
package com.android.car.cluster;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.car.Car;
import android.car.CarAppFocusManager;
+import android.car.cluster.CarInstrumentClusterManager;
+import android.car.cluster.IInstrumentClusterManagerCallback;
+import android.car.cluster.IInstrumentClusterManagerService;
import android.car.cluster.renderer.IInstrumentCluster;
+import android.car.cluster.renderer.IInstrumentClusterCallback;
import android.car.cluster.renderer.IInstrumentClusterNavigation;
+import android.car.cluster.renderer.InstrumentClusterRenderingService;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.Process;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import android.view.KeyEvent;
import com.android.car.AppFocusService;
@@ -40,6 +55,11 @@
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
* Service responsible for interaction with car's instrument cluster.
@@ -54,14 +74,23 @@
private static final Boolean DBG = false;
private final Context mContext;
+
private final AppFocusService mAppFocusService;
private final CarInputService mCarInputService;
+ private final PackageManager mPackageManager;
private final Object mSync = new Object();
+ private final ClusterServiceCallback mClusterCallback = new ClusterServiceCallback();
+ private final ClusterManagerService mClusterManagerService = new ClusterManagerService();
+
@GuardedBy("mSync")
private ContextOwner mNavContextOwner;
@GuardedBy("mSync")
private IInstrumentCluster mRendererService;
+ @GuardedBy("mSync")
+ private final HashMap<String, ClusterActivityInfo> mActivityInfoByCategory = new HashMap<>();
+ @GuardedBy("mSync")
+ private final HashMap<IBinder, ManagerCallbackInfo> mManagerCallbacks = new HashMap<>();
private boolean mRendererBound = false;
@@ -85,7 +114,9 @@
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected, name: " + name);
- mRendererService = null;
+ synchronized (mSync) {
+ mRendererService = null;
+ }
// Try to rebind with instrument cluster.
mRendererBound = bindInstrumentClusterRendererService();
}
@@ -96,6 +127,7 @@
mContext = context;
mAppFocusService = appFocusService;
mCarInputService = carInputService;
+ mPackageManager = mContext.getPackageManager();
}
@Override
@@ -188,6 +220,11 @@
Intent intent = new Intent();
intent.setComponent(ComponentName.unflattenFromString(rendererService));
+ Bundle extras = new Bundle();
+ extras.putBinder(
+ InstrumentClusterRenderingService.EXTRA_KEY_CALLBACK_SERVICE,
+ mClusterCallback);
+ intent.putExtras(extras);
return mContext.bindService(intent, mRendererServiceConnection, Context.BIND_AUTO_CREATE);
}
@@ -206,6 +243,10 @@
}
}
+ public IInstrumentClusterManagerService.Stub getManagerService() {
+ return mClusterManagerService;
+ }
+
@Override
public boolean onKeyEvent(KeyEvent event) {
if (DBG) {
@@ -236,4 +277,247 @@
this.pid = pid;
}
}
+
+ private static class ClusterActivityInfo {
+ Bundle launchOptions; // ActivityOptions
+ Bundle state; // ClusterActivityState
+ }
+
+ private void enforcePermission(String permission) {
+ int callingUid = Binder.getCallingUid();
+ int callingPid = Binder.getCallingPid();
+ if (Binder.getCallingUid() == Process.myUid()) {
+ if (mContext.checkCallingOrSelfPermission(permission) != PERMISSION_GRANTED) {
+ throw new SecurityException("Permission " + permission + " is not granted to "
+ + "client {uid: " + callingUid + ", pid: " + callingPid + "}");
+ }
+ }
+ }
+
+ private void enforceClusterControlPermission() {
+ enforcePermission(Car.PERMISSION_CAR_INSTRUMENT_CLUSTER_CONTROL);
+ }
+
+ private void doStartClusterActivity(Intent intent) {
+ enforceClusterControlPermission();
+
+ // Category from given intent should match category from cluster vendor implementation.
+ List<ResolveInfo> resolveList = mPackageManager.queryIntentActivities(intent,
+ PackageManager.GET_RESOLVED_FILTER);
+ if (resolveList == null || resolveList.isEmpty()) {
+ Log.w(TAG, "Failed to resolve an intent: " + intent);
+ return;
+ }
+
+ resolveList = checkPermission(resolveList, Car.PERMISSION_CAR_DISPLAY_IN_CLUSTER);
+ if (resolveList.isEmpty()) {
+ return;
+ }
+
+ // TODO(b/63861009): we may have multiple navigation apps that eligible to be launched in
+ // the cluster. We need to resolve intent that may have multiple activity candidates, right
+ // now we pickup the first one that matches registered category (resolveList is sorted
+ // priority).
+ Pair<ResolveInfo, ClusterActivityInfo> attributedResolveInfo =
+ findClusterActivityOptions(resolveList);
+ if (attributedResolveInfo == null) {
+ Log.w(TAG, "Unable to start an activity with intent: " + intent + " in the cluster: "
+ + "category intent didn't match with any categories from vendor "
+ + "implementation");
+ return;
+ }
+ ClusterActivityInfo opts = attributedResolveInfo.second;
+
+ // Intent was already checked for permission and resolved, make it explicit.
+ intent.setComponent(attributedResolveInfo.first.getComponentInfo().getComponentName());
+
+ intent.putExtra(CarInstrumentClusterManager.KEY_EXTRA_ACTIVITY_STATE, opts.state);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ // Virtual display could be private and not available to calling process.
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mContext.startActivity(intent, opts.launchOptions);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private List<ResolveInfo> checkPermission(List<ResolveInfo> resolveList,
+ String permission) {
+ List<ResolveInfo> permittedResolveList = new ArrayList<>(resolveList.size());
+ for (ResolveInfo info : resolveList) {
+ String pkgName = info.getComponentInfo().packageName;
+ if (mPackageManager.checkPermission(permission, pkgName) == PERMISSION_GRANTED) {
+ permittedResolveList.add(info);
+ } else {
+ Log.w(TAG, "Permission " + permission + " not granted for "
+ + info.getComponentInfo());
+ }
+
+ }
+ return permittedResolveList;
+ }
+
+ private void doRegisterManagerCallback(IInstrumentClusterManagerCallback callback)
+ throws RemoteException {
+ enforceClusterControlPermission();
+ IBinder binder = callback.asBinder();
+
+ List<Pair<String, Bundle>> knownActivityStates = null;
+ ManagerCallbackDeathRecipient deathRecipient = new ManagerCallbackDeathRecipient(binder);
+ synchronized (mSync) {
+ if (mManagerCallbacks.containsKey(binder)) {
+ Log.w(TAG, "Manager callback already registered for binder: " + binder);
+ return;
+ }
+ mManagerCallbacks.put(binder, new ManagerCallbackInfo(callback, deathRecipient));
+ if (!mActivityInfoByCategory.isEmpty()) {
+ knownActivityStates = new ArrayList<>(mActivityInfoByCategory.size());
+ for (Map.Entry<String, ClusterActivityInfo> it : mActivityInfoByCategory.entrySet()) {
+ knownActivityStates.add(new Pair<>(it.getKey(), it.getValue().state));
+ }
+ }
+ }
+ binder.linkToDeath(deathRecipient, 0);
+
+ // Notify manager immediately with known states.
+ if (knownActivityStates != null) {
+ for (Pair<String, Bundle> it : knownActivityStates) {
+ callback.setClusterActivityState(it.first, it.second);
+ }
+ }
+ }
+
+ private void doUnregisterManagerCallback(IBinder binder) throws RemoteException {
+ enforceClusterControlPermission();
+ ManagerCallbackInfo info;
+ synchronized (mSync) {
+ info = mManagerCallbacks.get(binder);
+ if (info == null) {
+ Log.w(TAG, "Unable to unregister manager callback binder: " + binder + " because "
+ + "it wasn't previously registered.");
+ return;
+ }
+ mManagerCallbacks.remove(binder);
+ }
+ binder.unlinkToDeath(info.deathRecipient, 0);
+ }
+
+ @Nullable
+ private Pair<ResolveInfo, ClusterActivityInfo> findClusterActivityOptions(
+ List<ResolveInfo> resolveList) {
+ synchronized (mSync) {
+ Set<String> registeredCategories = mActivityInfoByCategory.keySet();
+
+ for (ResolveInfo resolveInfo : resolveList) {
+ for (String category : registeredCategories) {
+ if (resolveInfo.filter != null && resolveInfo.filter.hasCategory(category)) {
+ ClusterActivityInfo categoryInfo = mActivityInfoByCategory.get(category);
+ return new Pair<>(resolveInfo, categoryInfo);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private class ManagerCallbackDeathRecipient implements DeathRecipient {
+ private final IBinder mBinder;
+
+ ManagerCallbackDeathRecipient(IBinder binder) {
+ mBinder = binder;
+ }
+
+ @Override
+ public void binderDied() {
+ try {
+ doUnregisterManagerCallback(mBinder);
+ } catch (RemoteException e) {
+ // Ignore, shutdown route.
+ }
+ }
+ }
+
+ private class ClusterManagerService extends IInstrumentClusterManagerService.Stub {
+
+ @Override
+ public void startClusterActivity(Intent intent) throws RemoteException {
+ doStartClusterActivity(intent);
+ }
+
+ @Override
+ public void registerCallback(IInstrumentClusterManagerCallback callback)
+ throws RemoteException {
+ doRegisterManagerCallback(callback);
+ }
+
+ @Override
+ public void unregisterCallback(IInstrumentClusterManagerCallback callback)
+ throws RemoteException {
+ doUnregisterManagerCallback(callback.asBinder());
+ }
+ }
+
+ private ClusterActivityInfo getOrCreateActivityInfoLocked(String category) {
+ return mActivityInfoByCategory.computeIfAbsent(category, k -> new ClusterActivityInfo());
+ }
+
+ /** This is communication channel from vendor cluster implementation to Car Service. */
+ private class ClusterServiceCallback extends IInstrumentClusterCallback.Stub {
+
+ @Override
+ public void setClusterActivityLaunchOptions(String category, Bundle activityOptions)
+ throws RemoteException {
+ doSetActivityLaunchOptions(category, activityOptions);
+ }
+
+ @Override
+ public void setClusterActivityState(String category, Bundle clusterActivityState)
+ throws RemoteException {
+ doSetClusterActivityState(category, clusterActivityState);
+ }
+ }
+
+ /** Called from cluster vendor implementation */
+ private void doSetActivityLaunchOptions(String category, Bundle activityOptions) {
+ if (DBG) {
+ Log.d(TAG, "doSetActivityLaunchOptions, category: " + category
+ + ", options: " + activityOptions);
+ }
+ synchronized (mSync) {
+ ClusterActivityInfo info = getOrCreateActivityInfoLocked(category);
+ info.launchOptions = activityOptions;
+ }
+ }
+
+ /** Called from cluster vendor implementation */
+ private void doSetClusterActivityState(String category, Bundle clusterActivityState)
+ throws RemoteException {
+ if (DBG) {
+ Log.d(TAG, "doSetClusterActivityState, category: " + category
+ + ", state: " + clusterActivityState);
+ }
+
+ List<ManagerCallbackInfo> managerCallbacks;
+ synchronized (mSync) {
+ ClusterActivityInfo info = getOrCreateActivityInfoLocked(category);
+ info.state = clusterActivityState;
+ managerCallbacks = new ArrayList<>(mManagerCallbacks.values());
+ }
+
+ for (ManagerCallbackInfo cbInfo : managerCallbacks) {
+ cbInfo.callback.setClusterActivityState(category, clusterActivityState);
+ }
+ }
+
+ private static class ManagerCallbackInfo {
+ final IInstrumentClusterManagerCallback callback;
+ final ManagerCallbackDeathRecipient deathRecipient;
+
+ ManagerCallbackInfo(IInstrumentClusterManagerCallback callback,
+ ManagerCallbackDeathRecipient deathRecipient) {
+ this.callback = callback;
+ this.deathRecipient = deathRecipient;
+ }
+ }
}
diff --git a/service/src/com/android/car/hal/AudioHalService.java b/service/src/com/android/car/hal/AudioHalService.java
index 14b8319..879bb6c 100644
--- a/service/src/com/android/car/hal/AudioHalService.java
+++ b/service/src/com/android/car/hal/AudioHalService.java
@@ -101,9 +101,12 @@
public static final int STREAM_NUM_DEFAULT = 0;
- public static final int FOCUS_STATE_ARRAY_INDEX_STATE = 0;
- public static final int FOCUS_STATE_ARRAY_INDEX_STREAMS = 1;
- public static final int FOCUS_STATE_ARRAY_INDEX_EXTERNAL_FOCUS = 2;
+ public static final int FOCUS_STATE_ARRAY_INDEX_STATE =
+ VehicleAudioFocusIndex.FOCUS;
+ public static final int FOCUS_STATE_ARRAY_INDEX_STREAMS =
+ VehicleAudioFocusIndex.STREAMS;
+ public static final int FOCUS_STATE_ARRAY_INDEX_EXTERNAL_FOCUS =
+ VehicleAudioFocusIndex.EXTERNAL_FOCUS_STATE;
public static final int AUDIO_CONTEXT_MUSIC_FLAG =
VehicleAudioContextFlag.MUSIC_FLAG;
@@ -131,6 +134,8 @@
VehicleAudioContextFlag.SYSTEM_SOUND_FLAG;
public static final int AUDIO_CONTEXT_EXT_SOURCE_FLAG =
VehicleAudioContextFlag.EXT_SOURCE_FLAG;
+ public static final int AUDIO_CONTEXT_RINGTONE_FLAG =
+ VehicleAudioContextFlag.RINGTONE_FLAG;
public interface AudioHalFocusListener {
/**
@@ -261,6 +266,8 @@
return VehicleAudioContextFlag.RADIO_FLAG;
case CarAudioManager.CAR_AUDIO_USAGE_VOICE_CALL:
return VehicleAudioContextFlag.CALL_FLAG;
+ case CarAudioManager.CAR_AUDIO_USAGE_RINGTONE:
+ return VehicleAudioContextFlag.RINGTONE_FLAG;
case CarAudioManager.CAR_AUDIO_USAGE_MUSIC:
return VehicleAudioContextFlag.MUSIC_FLAG;
case CarAudioManager.CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE:
@@ -281,15 +288,15 @@
if (extType != null) {
switch (extType) {
case CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_CD_DVD:
- return AudioHalService.AUDIO_CONTEXT_CD_ROM_FLAG;
+ return VehicleAudioContextFlag.CD_ROM_FLAG;
case CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_AUX_IN0:
case CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_AUX_IN1:
- return AudioHalService.AUDIO_CONTEXT_AUX_AUDIO_FLAG;
+ return VehicleAudioContextFlag.AUX_AUDIO_FLAG;
default:
if (extType.startsWith("RADIO_")) {
return VehicleAudioContextFlag.RADIO_FLAG;
} else {
- return AudioHalService.AUDIO_CONTEXT_EXT_SOURCE_FLAG;
+ return VehicleAudioContextFlag.EXT_SOURCE_FLAG;
}
}
} else { // no external source specified. fall back to radio
@@ -323,6 +330,8 @@
return CarAudioManager.CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE;
case VehicleAudioContextFlag.CALL_FLAG:
return CarAudioManager.CAR_AUDIO_USAGE_VOICE_CALL;
+ case VehicleAudioContextFlag.RINGTONE_FLAG:
+ return CarAudioManager.CAR_AUDIO_USAGE_RINGTONE;
case VehicleAudioContextFlag.CD_ROM_FLAG:
return CarAudioManager.CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE;
case VehicleAudioContextFlag.NOTIFICATION_FLAG:
@@ -476,7 +485,7 @@
VehiclePropValue propValue = mVehicleHal.get(VehicleProperty.AUDIO_FOCUS);
return toIntArray(propValue.value.int32Values);
} catch (PropertyTimeoutException e) {
- Log.e(CarLog.TAG_AUDIO, "VehicleProperty.AUDIO_HW_VARIANT not ready", e);
+ Log.e(CarLog.TAG_AUDIO, "VehicleProperty.AUDIO_FOCUS not ready", e);
return new int[] { VEHICLE_AUDIO_FOCUS_STATE_LOSS, 0x0, 0};
}
}
@@ -708,9 +717,9 @@
} break;
case AUDIO_VOLUME: {
ArrayList<Integer> vec = v.value.int32Values;
- int volume = vec.get(VehicleAudioVolumeIndex.INDEX_VOLUME);
- int streamNum = vec.get(VehicleAudioVolumeIndex.INDEX_STREAM);
- int volumeState = vec.get(VehicleAudioVolumeIndex.INDEX_STATE);
+ int streamNum = vec.get(VehicleAudioVolumeIndex.STREAM);
+ int volume = vec.get(VehicleAudioVolumeIndex.VOLUME);
+ int volumeState = vec.get(VehicleAudioVolumeIndex.STATE);
if (volumeListener != null) {
volumeListener.onVolumeChange(streamNum, volume, volumeState);
}
diff --git a/service/src/com/android/car/hal/DiagnosticHalService.java b/service/src/com/android/car/hal/DiagnosticHalService.java
index 98a3b3d..4ff0ada 100644
--- a/service/src/com/android/car/hal/DiagnosticHalService.java
+++ b/service/src/com/android/car/hal/DiagnosticHalService.java
@@ -17,26 +17,22 @@
package com.android.car.hal;
import android.annotation.Nullable;
-import android.car.annotation.FutureFeature;
-import android.car.hardware.CarDiagnosticEvent;
-import android.car.hardware.CarDiagnosticManager;
+import android.car.diagnostic.CarDiagnosticEvent;
+import android.car.diagnostic.CarDiagnosticManager;
import android.car.hardware.CarSensorManager;
import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
-import android.hardware.automotive.vehicle.V2_1.Obd2FloatSensorIndex;
-import android.hardware.automotive.vehicle.V2_1.Obd2IntegerSensorIndex;
-import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_0.DiagnosticFloatSensorIndex;
+import android.hardware.automotive.vehicle.V2_0.DiagnosticIntegerSensorIndex;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
import android.util.Log;
import android.util.SparseArray;
import com.android.car.CarLog;
import com.android.car.CarServiceUtils;
import com.android.car.vehiclehal.VehiclePropValueBuilder;
import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.BitSet;
-import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
@@ -45,7 +41,6 @@
* Diagnostic HAL service supporting gathering diagnostic info from VHAL and translating it into
* higher-level semantic information
*/
-@FutureFeature
public class DiagnosticHalService extends SensorHalServiceBase {
public static class DiagnosticCapabilities {
private final CopyOnWriteArraySet<Integer> mProperties = new CopyOnWriteArraySet<>();
@@ -129,7 +124,7 @@
}
private int getNumIntegerSensors(int halPropId) {
- int count = Obd2IntegerSensorIndex.LAST_SYSTEM_INDEX + 1;
+ int count = DiagnosticIntegerSensorIndex.LAST_SYSTEM_INDEX + 1;
List<Integer> configArray = getPropConfigArray(halPropId);
if(configArray.size() < 2) {
Log.e(CarLog.TAG_DIAGNOSTIC, String.format(
@@ -143,7 +138,7 @@
}
private int getNumFloatSensors(int halPropId) {
- int count = Obd2FloatSensorIndex.LAST_SYSTEM_INDEX + 1;
+ int count = DiagnosticFloatSensorIndex.LAST_SYSTEM_INDEX + 1;
List<Integer> configArray = getPropConfigArray(halPropId);
if(configArray.size() < 2) {
Log.e(CarLog.TAG_DIAGNOSTIC, String.format(
@@ -185,7 +180,7 @@
}
}
- builder.withDTC(value.value.stringValue);
+ builder.withDtc(value.value.stringValue);
return builder.build();
}
diff --git a/service/src/com/android/car/hal/HvacHalService.java b/service/src/com/android/car/hal/HvacHalService.java
index d364850..77ab1be 100644
--- a/service/src/com/android/car/hal/HvacHalService.java
+++ b/service/src/com/android/car/hal/HvacHalService.java
@@ -23,44 +23,46 @@
private static final String TAG = "HvacHalService";
private final ManagerToHalPropIdMap mMgrHalPropIdMap = ManagerToHalPropIdMap.create(
- CarHvacManager.ID_MIRROR_DEFROSTER_ON, VehicleProperty.HVAC_SIDE_MIRROR_HEAT,
+ CarHvacManager.ID_MIRROR_DEFROSTER_ON, VehicleProperty.HVAC_SIDE_MIRROR_HEAT,
- CarHvacManager.ID_STEERING_WHEEL_TEMP, VehicleProperty.HVAC_STEERING_WHEEL_TEMP,
+ CarHvacManager.ID_STEERING_WHEEL_TEMP, VehicleProperty.HVAC_STEERING_WHEEL_TEMP,
- CarHvacManager.ID_OUTSIDE_AIR_TEMP, VehicleProperty.ENV_OUTSIDE_TEMPERATURE,
+ CarHvacManager.ID_OUTSIDE_AIR_TEMP, VehicleProperty.ENV_OUTSIDE_TEMPERATURE,
- CarHvacManager.ID_TEMPERATURE_UNITS, VehicleProperty.HVAC_TEMPERATURE_UNITS,
+ CarHvacManager.ID_TEMPERATURE_UNITS, VehicleProperty.HVAC_TEMPERATURE_UNITS,
- CarHvacManager.ID_ZONED_TEMP_SETPOINT, VehicleProperty.HVAC_TEMPERATURE_SET,
+ CarHvacManager.ID_ZONED_TEMP_SETPOINT, VehicleProperty.HVAC_TEMPERATURE_SET,
- CarHvacManager.ID_ZONED_TEMP_ACTUAL, VehicleProperty.HVAC_TEMPERATURE_CURRENT,
+ CarHvacManager.ID_ZONED_TEMP_ACTUAL, VehicleProperty.HVAC_TEMPERATURE_CURRENT,
- CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, VehicleProperty.HVAC_FAN_SPEED,
+ CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, VehicleProperty.HVAC_FAN_SPEED,
- CarHvacManager.ID_ZONED_FAN_SPEED_RPM, VehicleProperty.HVAC_ACTUAL_FAN_SPEED_RPM,
+ CarHvacManager.ID_ZONED_FAN_SPEED_RPM, VehicleProperty.HVAC_ACTUAL_FAN_SPEED_RPM,
- CarHvacManager.ID_ZONED_FAN_POSITION_AVAILABLE,
- VehicleProperty.HVAC_FAN_DIRECTION_AVAILABLE,
+ CarHvacManager.ID_ZONED_FAN_POSITION_AVAILABLE,
+ VehicleProperty.HVAC_FAN_DIRECTION_AVAILABLE,
- CarHvacManager.ID_ZONED_FAN_POSITION, VehicleProperty.HVAC_FAN_DIRECTION,
+ CarHvacManager.ID_ZONED_FAN_POSITION, VehicleProperty.HVAC_FAN_DIRECTION,
- CarHvacManager.ID_ZONED_SEAT_TEMP, VehicleProperty.HVAC_SEAT_TEMPERATURE,
+ CarHvacManager.ID_ZONED_SEAT_TEMP, VehicleProperty.HVAC_SEAT_TEMPERATURE,
- CarHvacManager.ID_ZONED_AC_ON, VehicleProperty.HVAC_AC_ON,
+ CarHvacManager.ID_ZONED_AC_ON, VehicleProperty.HVAC_AC_ON,
- CarHvacManager.ID_ZONED_AUTOMATIC_MODE_ON, VehicleProperty.HVAC_AUTO_ON,
+ CarHvacManager.ID_ZONED_AUTOMATIC_MODE_ON, VehicleProperty.HVAC_AUTO_ON,
- CarHvacManager.ID_ZONED_AIR_RECIRCULATION_ON,VehicleProperty.HVAC_RECIRC_ON,
+ CarHvacManager.ID_ZONED_AIR_RECIRCULATION_ON,VehicleProperty.HVAC_RECIRC_ON,
- CarHvacManager.ID_ZONED_MAX_AC_ON, VehicleProperty.HVAC_MAX_AC_ON,
+ CarHvacManager.ID_ZONED_MAX_AC_ON, VehicleProperty.HVAC_MAX_AC_ON,
- CarHvacManager.ID_ZONED_DUAL_ZONE_ON, VehicleProperty.HVAC_DUAL_ON,
+ CarHvacManager.ID_ZONED_DUAL_ZONE_ON, VehicleProperty.HVAC_DUAL_ON,
- CarHvacManager.ID_ZONED_MAX_DEFROST_ON, VehicleProperty.HVAC_MAX_DEFROST_ON,
+ CarHvacManager.ID_ZONED_MAX_DEFROST_ON, VehicleProperty.HVAC_MAX_DEFROST_ON,
- CarHvacManager.ID_ZONED_HVAC_POWER_ON, VehicleProperty.HVAC_POWER_ON,
+ CarHvacManager.ID_ZONED_HVAC_POWER_ON, VehicleProperty.HVAC_POWER_ON,
- CarHvacManager.ID_WINDOW_DEFROSTER_ON, VehicleProperty.HVAC_DEFROSTER
+ CarHvacManager.ID_ZONED_HVAC_AUTO_RECIRC_ON, VehicleProperty.HVAC_AUTO_RECIRC_ON,
+
+ CarHvacManager.ID_WINDOW_DEFROSTER_ON, VehicleProperty.HVAC_DEFROSTER
);
public HvacHalService(VehicleHal vehicleHal) {
diff --git a/service/src/com/android/car/hal/SensorHalService.java b/service/src/com/android/car/hal/SensorHalService.java
index a4fd77d..6ee56a4 100644
--- a/service/src/com/android/car/hal/SensorHalService.java
+++ b/service/src/com/android/car/hal/SensorHalService.java
@@ -19,6 +19,7 @@
import static java.lang.Integer.toHexString;
import android.annotation.Nullable;
+import android.car.hardware.CarSensorConfig;
import android.car.hardware.CarSensorEvent;
import android.car.hardware.CarSensorManager;
import android.hardware.automotive.vehicle.V2_0.VehicleGear;
@@ -29,11 +30,13 @@
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType;
+import android.os.Bundle;
import android.util.Log;
import android.util.SparseIntArray;
import com.android.car.CarLog;
import com.android.car.CarSensorEventFactory;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
@@ -50,6 +53,7 @@
public interface SensorListener {
/**
* Sensor events are available.
+ *
* @param events
*/
void onSensorEvents(List<CarSensorEvent> events);
@@ -57,42 +61,67 @@
// Manager property Id to HAL property Id mapping.
private final static ManagerToHalPropIdMap mManagerToHalPropIdMap =
- ManagerToHalPropIdMap.create(
- CarSensorManager.SENSOR_TYPE_CAR_SPEED, VehicleProperty.PERF_VEHICLE_SPEED,
- CarSensorManager.SENSOR_TYPE_RPM, VehicleProperty.ENGINE_RPM,
- CarSensorManager.SENSOR_TYPE_ODOMETER, VehicleProperty.PERF_ODOMETER,
- CarSensorManager.SENSOR_TYPE_GEAR, VehicleProperty.GEAR_SELECTION,
- CarSensorManager.SENSOR_TYPE_NIGHT, VehicleProperty.NIGHT_MODE,
- CarSensorManager.SENSOR_TYPE_PARKING_BRAKE, VehicleProperty.PARKING_BRAKE_ON,
- CarSensorManager.SENSOR_TYPE_DRIVING_STATUS, VehicleProperty.DRIVING_STATUS,
- CarSensorManager.SENSOR_TYPE_FUEL_LEVEL, VehicleProperty.FUEL_LEVEL_LOW,
- CarSensorManager.SENSOR_TYPE_IGNITION_STATE, VehicleProperty.IGNITION_STATE);
+ ManagerToHalPropIdMap.create(
+ CarSensorManager.SENSOR_TYPE_CAR_SPEED, VehicleProperty.PERF_VEHICLE_SPEED,
+ CarSensorManager.SENSOR_TYPE_RPM, VehicleProperty.ENGINE_RPM,
+ CarSensorManager.SENSOR_TYPE_ODOMETER, VehicleProperty.PERF_ODOMETER,
+ CarSensorManager.SENSOR_TYPE_GEAR, VehicleProperty.GEAR_SELECTION,
+ CarSensorManager.SENSOR_TYPE_NIGHT, VehicleProperty.NIGHT_MODE,
+ CarSensorManager.SENSOR_TYPE_PARKING_BRAKE, VehicleProperty.PARKING_BRAKE_ON,
+ CarSensorManager.SENSOR_TYPE_DRIVING_STATUS, VehicleProperty.DRIVING_STATUS,
+ CarSensorManager.SENSOR_TYPE_FUEL_LEVEL, VehicleProperty.FUEL_LEVEL_LOW,
+ CarSensorManager.SENSOR_TYPE_IGNITION_STATE, VehicleProperty.IGNITION_STATE,
+ CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE, VehicleProperty.WHEEL_TICK,
+ CarSensorManager.SENSOR_TYPE_ABS_ACTIVE, VehicleProperty.ABS_ACTIVE,
+ CarSensorManager.SENSOR_TYPE_TRACTION_CONTROL_ACTIVE,
+ VehicleProperty.TRACTION_CONTROL_ACTIVE
+ );
private final static SparseIntArray mMgrGearToHalMap = initSparseIntArray(
- VehicleGear.GEAR_NEUTRAL, CarSensorEvent.GEAR_NEUTRAL,
- VehicleGear.GEAR_REVERSE, CarSensorEvent.GEAR_REVERSE,
- VehicleGear.GEAR_PARK, CarSensorEvent.GEAR_PARK,
- VehicleGear.GEAR_DRIVE, CarSensorEvent.GEAR_DRIVE,
- VehicleGear.GEAR_LOW, CarSensorEvent.GEAR_FIRST, // Also GEAR_1 - the value is the same.
- VehicleGear.GEAR_2, CarSensorEvent.GEAR_SECOND,
- VehicleGear.GEAR_3, CarSensorEvent.GEAR_THIRD,
- VehicleGear.GEAR_4, CarSensorEvent.GEAR_FOURTH,
- VehicleGear.GEAR_5, CarSensorEvent.GEAR_FIFTH,
- VehicleGear.GEAR_6, CarSensorEvent.GEAR_SIXTH,
- VehicleGear.GEAR_7, CarSensorEvent.GEAR_SEVENTH,
- VehicleGear.GEAR_8, CarSensorEvent.GEAR_EIGHTH,
- VehicleGear.GEAR_9, CarSensorEvent.GEAR_NINTH);
+ VehicleGear.GEAR_NEUTRAL, CarSensorEvent.GEAR_NEUTRAL,
+ VehicleGear.GEAR_REVERSE, CarSensorEvent.GEAR_REVERSE,
+ VehicleGear.GEAR_PARK, CarSensorEvent.GEAR_PARK,
+ VehicleGear.GEAR_DRIVE, CarSensorEvent.GEAR_DRIVE,
+ VehicleGear.GEAR_LOW, CarSensorEvent.GEAR_FIRST, // Also GEAR_1 - the value is the same.
+ VehicleGear.GEAR_2, CarSensorEvent.GEAR_SECOND,
+ VehicleGear.GEAR_3, CarSensorEvent.GEAR_THIRD,
+ VehicleGear.GEAR_4, CarSensorEvent.GEAR_FOURTH,
+ VehicleGear.GEAR_5, CarSensorEvent.GEAR_FIFTH,
+ VehicleGear.GEAR_6, CarSensorEvent.GEAR_SIXTH,
+ VehicleGear.GEAR_7, CarSensorEvent.GEAR_SEVENTH,
+ VehicleGear.GEAR_8, CarSensorEvent.GEAR_EIGHTH,
+ VehicleGear.GEAR_9, CarSensorEvent.GEAR_NINTH);
private final static SparseIntArray mMgrIgnitionStateToHalMap = initSparseIntArray(
- VehicleIgnitionState.UNDEFINED, CarSensorEvent.IGNITION_STATE_UNDEFINED,
- VehicleIgnitionState.LOCK, CarSensorEvent.IGNITION_STATE_LOCK,
- VehicleIgnitionState.OFF, CarSensorEvent.IGNITION_STATE_OFF,
- VehicleIgnitionState.ACC, CarSensorEvent.IGNITION_STATE_ACC,
- VehicleIgnitionState.ON, CarSensorEvent.IGNITION_STATE_ON,
- VehicleIgnitionState.START, CarSensorEvent.IGNITION_STATE_START);
+ VehicleIgnitionState.UNDEFINED, CarSensorEvent.IGNITION_STATE_UNDEFINED,
+ VehicleIgnitionState.LOCK, CarSensorEvent.IGNITION_STATE_LOCK,
+ VehicleIgnitionState.OFF, CarSensorEvent.IGNITION_STATE_OFF,
+ VehicleIgnitionState.ACC, CarSensorEvent.IGNITION_STATE_ACC,
+ VehicleIgnitionState.ON, CarSensorEvent.IGNITION_STATE_ON,
+ VehicleIgnitionState.START, CarSensorEvent.IGNITION_STATE_START);
private SensorListener mSensorListener;
+ private int[] mMicrometersPerWheelTick = {0, 0, 0, 0};
+
+ @Override
+ public void init() {
+ VehiclePropConfig config;
+ // Populate internal values if available
+ synchronized (this) {
+ config = mSensorToPropConfig.get(CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE);
+ }
+ if (config == null) {
+ Log.e(TAG, "init: unable to get property config for SENSOR_TYPE_WHEEL_TICK_DISTANCE");
+ } else {
+ for (int i = 0; i < 4; i++) {
+ mMicrometersPerWheelTick[i] = config.configArray.get(i +
+ INDEX_WHEEL_DISTANCE_FRONT_LEFT);
+ }
+ }
+ super.init();
+ }
+
public SensorHalService(VehicleHal hal) {
super(hal);
}
@@ -114,6 +143,7 @@
// Should be used only inside handleHalEvents method.
private final LinkedList<CarSensorEvent> mEventsToDispatch = new LinkedList<>();
+
@Override
public void handleHalEvents(List<VehiclePropValue> values) {
for (VehiclePropValue v : values) {
@@ -142,7 +172,7 @@
mgrValue = mMgrGearToHalMap.get(halValue, -1);
break;
case VehicleProperty.IGNITION_STATE:
- mgrValue = mMgrIgnitionStateToHalMap.get(halValue, -1);
+ mgrValue = mMgrIgnitionStateToHalMap.get(halValue, -1);
default:
break; // Do nothing
}
@@ -156,27 +186,45 @@
if (sensorType == SENSOR_TYPE_INVALID) {
throw new RuntimeException("no sensor defined for property 0x" + toHexString(property));
}
-
+ // Handle the valid sensor
int dataType = property & VehiclePropertyType.MASK;
-
CarSensorEvent event = null;
switch (dataType) {
case VehiclePropertyType.BOOLEAN:
event = CarSensorEventFactory.createBooleanEvent(sensorType, v.timestamp,
- v.value.int32Values.get(0) == 1);
+ v.value.int32Values.get(0) == 1);
+ break;
+ case VehiclePropertyType.COMPLEX:
+ event = CarSensorEventFactory.createComplexEvent(sensorType, v.timestamp, v);
break;
case VehiclePropertyType.INT32:
Integer mgrVal = mapHalEnumValueToMgr(property, v.value.int32Values.get(0));
- event = mgrVal == null ? null
- : CarSensorEventFactory.createIntEvent(sensorType, v.timestamp, mgrVal);
+ event = mgrVal == null ? null
+ : CarSensorEventFactory.createIntEvent(sensorType, v.timestamp, mgrVal);
break;
- case VehiclePropertyType.FLOAT: {
+ case VehiclePropertyType.FLOAT:
event = CarSensorEventFactory.createFloatEvent(sensorType, v.timestamp,
- v.value.floatValues.get(0));
+ v.value.floatValues.get(0));
break;
- }
default:
Log.w(TAG, "createCarSensorEvent: unsupported type: 0x" + toHexString(dataType));
+ break;
+ }
+ // Perform property specific actions
+ switch (property) {
+ case VehicleProperty.WHEEL_TICK:
+ // Apply the um/tick scaling factor, then divide by 1000 to generate mm
+ for (int i = 0; i < 4; i++) {
+ // ResetCounts is at longValues[0]
+ if (event.longValues[i + CarSensorEvent.INDEX_WHEEL_DISTANCE_FRONT_LEFT] !=
+ Long.MAX_VALUE) {
+ event.longValues[i + CarSensorEvent.INDEX_WHEEL_DISTANCE_FRONT_LEFT] *=
+ mMicrometersPerWheelTick[i];
+ event.longValues[i + CarSensorEvent.INDEX_WHEEL_DISTANCE_FRONT_LEFT] /=
+ 1000;
+ }
+ }
+ break;
}
if (DBG_EVENTS) Log.i(TAG, "Sensor event created: " + event);
return event;
@@ -225,6 +273,9 @@
for (int i = 0; i < mSensorToPropConfig.size(); i++) {
writer.println(mSensorToPropConfig.valueAt(i).toString());
}
+ for (int i = 0; i < mMicrometersPerWheelTick.length; i++) {
+ writer.println("mMicrometersPerWheelTick[" + i + "] = " + mMicrometersPerWheelTick[i]);
+ }
}
private static SparseIntArray initSparseIntArray(int... keyValuePairs) {
@@ -239,4 +290,50 @@
}
return map;
}
-}
+
+ private static final int INDEX_WHEEL_DISTANCE_ENABLE_FLAG = 0;
+ private static final int INDEX_WHEEL_DISTANCE_FRONT_LEFT = 1;
+ private static final int INDEX_WHEEL_DISTANCE_FRONT_RIGHT = 2;
+ private static final int INDEX_WHEEL_DISTANCE_REAR_RIGHT = 3;
+ private static final int INDEX_WHEEL_DISTANCE_REAR_LEFT = 4;
+ private static final int WHEEL_TICK_DISTANCE_BUNDLE_SIZE = 6;
+
+ private Bundle createWheelDistanceTickBundle(ArrayList<Integer> configArray) {
+ Bundle b = new Bundle(WHEEL_TICK_DISTANCE_BUNDLE_SIZE);
+ b.putInt(CarSensorConfig.WHEEL_TICK_DISTANCE_SUPPORTED_WHEELS,
+ configArray.get(INDEX_WHEEL_DISTANCE_ENABLE_FLAG));
+ b.putInt(CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_LEFT_UM_PER_TICK,
+ configArray.get(INDEX_WHEEL_DISTANCE_FRONT_LEFT));
+ b.putInt(CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_RIGHT_UM_PER_TICK,
+ configArray.get(INDEX_WHEEL_DISTANCE_FRONT_RIGHT));
+ b.putInt(CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_RIGHT_UM_PER_TICK,
+ configArray.get(INDEX_WHEEL_DISTANCE_REAR_RIGHT));
+ b.putInt(CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_LEFT_UM_PER_TICK,
+ configArray.get(INDEX_WHEEL_DISTANCE_REAR_LEFT));
+ return b;
+ }
+
+
+ public CarSensorConfig getSensorConfig(int sensorType) {
+ VehiclePropConfig cfg;
+ synchronized (this) {
+ cfg = mSensorToPropConfig.get(sensorType);
+ }
+ if (cfg == null) {
+ /* Invalid sensor type. */
+ throw new IllegalArgumentException("Unknown sensorType = " + sensorType);
+ } else {
+ Bundle b;
+ switch(sensorType) {
+ case CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE:
+ b = createWheelDistanceTickBundle(cfg.configArray);
+ break;
+ default:
+ /* Unhandled config. Create empty bundle */
+ b = Bundle.EMPTY;
+ break;
+ }
+ return new CarSensorConfig(sensorType, b);
+ }
+ }
+}
\ No newline at end of file
diff --git a/service/src/com/android/car/hal/VehicleHal.java b/service/src/com/android/car/hal/VehicleHal.java
index c60d16d..21f8f51 100644
--- a/service/src/com/android/car/hal/VehicleHal.java
+++ b/service/src/com/android/car/hal/VehicleHal.java
@@ -78,11 +78,11 @@
private final HvacHalService mHvacHal;
private final InputHalService mInputHal;
private final VendorExtensionHalService mVendorExtensionHal;
+ private DiagnosticHalService mDiagnosticHal = null;
+
@FutureFeature
private VmsHalService mVmsHal;
- @FutureFeature
- private DiagnosticHalService mDiagnosticHal = null;
/** Might be re-assigned if Vehicle HAL is reconnected. */
private volatile HalClient mHalClient;
@@ -155,7 +155,6 @@
/** Dummy version only for testing */
@VisibleForTesting
- @FutureFeature
public VehicleHal(PowerHalService powerHal, SensorHalService sensorHal, InfoHalService infoHal,
AudioHalService audioHal, CabinHalService cabinHal, DiagnosticHalService diagnosticHal,
RadioHalService radioHal, HvacHalService hvacHal, HalClient halClient) {
@@ -260,7 +259,6 @@
return mCabinHal;
}
- @FutureFeature
public DiagnosticHalService getDiagnosticHal() { return mDiagnosticHal; }
public RadioHalService getRadioHal() {
diff --git a/service/src/com/android/car/hal/VmsHalService.java b/service/src/com/android/car/hal/VmsHalService.java
index 8ab5427..d20a453 100644
--- a/service/src/com/android/car/hal/VmsHalService.java
+++ b/service/src/com/android/car/hal/VmsHalService.java
@@ -22,16 +22,19 @@
import android.car.annotation.FutureFeature;
import android.car.vms.IVmsSubscriberClient;
import android.car.vms.VmsLayer;
+import android.car.vms.VmsAssociatedLayer;
import android.car.vms.VmsLayerDependency;
import android.car.vms.VmsLayersOffering;
+import android.car.vms.VmsOperationRecorder;
import android.car.vms.VmsSubscriptionState;
import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
-import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
-import android.hardware.automotive.vehicle.V2_1.VmsBaseMessageIntegerValuesIndex;
-import android.hardware.automotive.vehicle.V2_1.VmsMessageType;
-import android.hardware.automotive.vehicle.V2_1.VmsOfferingMessageIntegerValuesIndex;
-import android.hardware.automotive.vehicle.V2_1.VmsSimpleMessageIntegerValuesIndex;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_0.VmsBaseMessageIntegerValuesIndex;
+import android.hardware.automotive.vehicle.V2_0.VmsMessageType;
+import android.hardware.automotive.vehicle.V2_0.VmsMessageWithLayerAndPublisherIdIntegerValuesIndex;
+import android.hardware.automotive.vehicle.V2_0.VmsOfferingMessageIntegerValuesIndex;
+import android.hardware.automotive.vehicle.V2_0.VmsMessageWithLayerIntegerValuesIndex;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
@@ -40,7 +43,9 @@
import com.android.car.VmsPublishersInfo;
import com.android.car.VmsRouting;
import com.android.internal.annotations.GuardedBy;
+
import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -63,11 +68,16 @@
private static final int HAL_PROPERTY_ID = VehicleProperty.VEHICLE_MAP_SERVICE;
private static final String TAG = "VmsHalService";
+ private final static List<Integer> AVAILABILITY_MESSAGE_TYPES = Collections.unmodifiableList(
+ Arrays.asList(
+ VmsMessageType.AVAILABILITY_RESPONSE,
+ VmsMessageType.AVAILABILITY_CHANGE));
+
private boolean mIsSupported = false;
private CopyOnWriteArrayList<VmsHalPublisherListener> mPublisherListeners =
- new CopyOnWriteArrayList<>();
+ new CopyOnWriteArrayList<>();
private CopyOnWriteArrayList<VmsHalSubscriberListener> mSubscriberListeners =
- new CopyOnWriteArrayList<>();
+ new CopyOnWriteArrayList<>();
private final IBinder mHalPublisherToken = new Binder();
private final VehicleHal mVehicleHal;
@@ -91,11 +101,11 @@
* The VmsSubscriberService implements this interface to receive data from the HAL.
*/
public interface VmsHalSubscriberListener {
- // Notify listener on a data Message.
- void onDataMessage(VmsLayer layer, byte[] payload);
+ // Notifies the listener on a data Message from a publisher.
+ void onDataMessage(VmsLayer layer, int publisherId, byte[] payload);
- // Notify listener on a change in available layers.
- void onLayersAvaiabilityChange(List<VmsLayer> availableLayers);
+ // Notifies the listener on a change in available layers.
+ void onLayersAvaiabilityChange(List<VmsAssociatedLayer> availableLayers);
}
/**
@@ -134,7 +144,8 @@
mRouting.addSubscription(listener, layer);
}
if (firstSubscriptionForLayer) {
- notifyPublishers(layer, true);
+ notifyHalPublishers(layer, true);
+ notifyClientPublishers();
}
}
@@ -153,7 +164,8 @@
layerHasSubscribers = mRouting.hasLayerSubscriptions(layer);
}
if (!layerHasSubscribers) {
- notifyPublishers(layer, false);
+ notifyHalPublishers(layer, false);
+ notifyClientPublishers();
}
}
@@ -169,21 +181,60 @@
}
}
- public void removeDeadListener(IVmsSubscriberClient listener) {
+ public void addSubscription(IVmsSubscriberClient listener, VmsLayer layer, int publisherId) {
+ boolean firstSubscriptionForLayer = false;
synchronized (mLock) {
- mRouting.removeDeadListener(listener);
+ // Check if publishers need to be notified about this change in subscriptions.
+ firstSubscriptionForLayer = !(mRouting.hasLayerSubscriptions(layer) ||
+ mRouting.hasLayerFromPublisherSubscriptions(layer, publisherId));
+
+ // Add the listeners subscription to the layer
+ mRouting.addSubscription(listener, layer, publisherId);
+ }
+ if (firstSubscriptionForLayer) {
+ notifyHalPublishers(layer, true);
+ notifyClientPublishers();
}
}
- public Set<IVmsSubscriberClient> getListeners(VmsLayer layer) {
+ public void removeSubscription(IVmsSubscriberClient listener, VmsLayer layer, int publisherId) {
+ boolean layerHasSubscribers = true;
synchronized (mLock) {
- return mRouting.getListeners(layer);
+ if (!mRouting.hasLayerFromPublisherSubscriptions(layer, publisherId)) {
+ Log.i(TAG, "Trying to remove a layer with no subscription: " +
+ layer + ", publisher ID:" + publisherId);
+ return;
+ }
+
+ // Remove the listeners subscription to the layer
+ mRouting.removeSubscription(listener, layer, publisherId);
+
+ // Check if publishers need to be notified about this change in subscriptions.
+ layerHasSubscribers = mRouting.hasLayerSubscriptions(layer) ||
+ mRouting.hasLayerFromPublisherSubscriptions(layer, publisherId);
+ }
+ if (!layerHasSubscribers) {
+ notifyHalPublishers(layer, false);
+ notifyClientPublishers();
}
}
- public Set<IVmsSubscriberClient> getAllListeners() {
+ public void removeDeadSubscriber(IVmsSubscriberClient listener) {
synchronized (mLock) {
- return mRouting.getAllListeners();
+ mRouting.removeDeadSubscriber(listener);
+ }
+ }
+
+ public Set<IVmsSubscriberClient> getSubscribersForLayerFromPublisher(VmsLayer layer,
+ int publisherId) {
+ synchronized (mLock) {
+ return mRouting.getSubscribersForLayerFromPublisher(layer, publisherId);
+ }
+ }
+
+ public Set<IVmsSubscriberClient> getAllSubscribers() {
+ synchronized (mLock) {
+ return mRouting.getAllSubscribers();
}
}
@@ -205,7 +256,7 @@
* The publisherInfo should be static for a binary and should only change as part of a software
* update. The publisherInfo is a serialized proto message which VMS clients can interpret.
*/
- public int getPublisherStaticId(byte[] publisherInfo) {
+ public int getPublisherId(byte[] publisherInfo) {
if (DBG) {
Log.i(TAG, "Getting publisher static ID");
}
@@ -223,7 +274,7 @@
}
}
- public void addHalSubscription(VmsLayer layer) {
+ private void addHalSubscription(VmsLayer layer) {
boolean firstSubscriptionForLayer = true;
synchronized (mLock) {
// Check if publishers need to be notified about this change in subscriptions.
@@ -233,11 +284,28 @@
mRouting.addHalSubscription(layer);
}
if (firstSubscriptionForLayer) {
- notifyPublishers(layer, true);
+ notifyHalPublishers(layer, true);
+ notifyClientPublishers();
}
}
- public void removeHalSubscription(VmsLayer layer) {
+ private void addHalSubscriptionToPublisher(VmsLayer layer, int publisherId) {
+ boolean firstSubscriptionForLayer = true;
+ synchronized (mLock) {
+ // Check if publishers need to be notified about this change in subscriptions.
+ firstSubscriptionForLayer = !(mRouting.hasLayerSubscriptions(layer) ||
+ mRouting.hasLayerFromPublisherSubscriptions(layer, publisherId));
+
+ // Add the listeners subscription to the layer
+ mRouting.addHalSubscriptionToPublisher(layer, publisherId);
+ }
+ if (firstSubscriptionForLayer) {
+ notifyHalPublishers(layer, publisherId, true);
+ notifyClientPublishers();
+ }
+ }
+
+ private void removeHalSubscription(VmsLayer layer) {
boolean layerHasSubscribers = true;
synchronized (mLock) {
if (!mRouting.hasLayerSubscriptions(layer)) {
@@ -252,26 +320,49 @@
layerHasSubscribers = mRouting.hasLayerSubscriptions(layer);
}
if (!layerHasSubscribers) {
- notifyPublishers(layer, false);
+ notifyHalPublishers(layer, false);
+ notifyClientPublishers();
}
}
- public boolean containsListener(IVmsSubscriberClient listener) {
+ public void removeHalSubscriptionFromPublisher(VmsLayer layer, int publisherId) {
+ boolean layerHasSubscribers = true;
synchronized (mLock) {
- return mRouting.containsListener(listener);
+ if (!mRouting.hasLayerSubscriptions(layer)) {
+ Log.i(TAG, "Trying to remove a layer with no subscription: " + layer);
+ return;
+ }
+
+ // Remove the listeners subscription to the layer
+ mRouting.removeHalSubscriptionToPublisher(layer, publisherId);
+
+ // Check if publishers need to be notified about this change in subscriptions.
+ layerHasSubscribers = mRouting.hasLayerSubscriptions(layer) ||
+ mRouting.hasLayerFromPublisherSubscriptions(layer, publisherId);
+ }
+ if (!layerHasSubscribers) {
+ notifyHalPublishers(layer, publisherId, false);
+ notifyClientPublishers();
}
}
- public void setPublisherLayersOffering(IBinder publisherToken, VmsLayersOffering offering){
- Set<VmsLayer> availableLayers = Collections.EMPTY_SET;
+ public boolean containsSubscriber(IVmsSubscriberClient subscriber) {
+ synchronized (mLock) {
+ return mRouting.containsSubscriber(subscriber);
+ }
+ }
+
+ public void setPublisherLayersOffering(IBinder publisherToken, VmsLayersOffering offering) {
+ Set<VmsAssociatedLayer> availableLayers = Collections.EMPTY_SET;
synchronized (mLock) {
updateOffering(publisherToken, offering);
+ VmsOperationRecorder.get().setPublisherLayersOffering(offering);
availableLayers = mAvailableLayers.getAvailableLayers();
}
- notifySubscribers(availableLayers);
+ notifyOfAvailabilityChange(availableLayers);
}
- public Set<VmsLayer> getAvailableLayers() {
+ public Set<VmsAssociatedLayer> getAvailableLayers() {
//TODO(b/36872877): wrap available layers in VmsAvailabilityState similar to VmsSubscriptionState.
synchronized (mLock) {
return mAvailableLayers.getAvailableLayers();
@@ -285,10 +376,17 @@
* @param layer layer which is being subscribed to or unsubscribed from.
* @param hasSubscribers indicates if the notification is for subscription or unsubscription.
*/
- private void notifyPublishers(VmsLayer layer, boolean hasSubscribers) {
+ private void notifyHalPublishers(VmsLayer layer, boolean hasSubscribers) {
// notify the HAL
setSubscriptionRequest(layer, hasSubscribers);
+ }
+ private void notifyHalPublishers(VmsLayer layer, int publisherId, boolean hasSubscribers) {
+ // notify the HAL
+ setSubscriptionToPublisherRequest(layer, publisherId, hasSubscribers);
+ }
+
+ private void notifyClientPublishers() {
// Notify the App publishers
for (VmsHalPublisherListener listener : mPublisherListeners) {
// Besides the list of layers, also a timestamp is provided to the clients.
@@ -303,9 +401,9 @@
*
* @param availableLayers the layers which publishers claim they made publish.
*/
- private void notifySubscribers(Set<VmsLayer> availableLayers) {
+ private void notifyOfAvailabilityChange(Set<VmsAssociatedLayer> availableLayers) {
// notify the HAL
- setAvailableLayers(availableLayers);
+ notifyAvailabilityChangeToHal(availableLayers);
// Notify the App subscribers
for (VmsHalSubscriberListener listener : mSubscriberListeners) {
@@ -363,12 +461,12 @@
}
for (VehiclePropValue v : values) {
ArrayList<Integer> vec = v.value.int32Values;
- int messageType = vec.get(VmsBaseMessageIntegerValuesIndex.VMS_MESSAGE_TYPE);
+ int messageType = vec.get(VmsBaseMessageIntegerValuesIndex.MESSAGE_TYPE);
if (DBG) {
Log.d(TAG, "Handling VMS message type: " + messageType);
}
- switch(messageType) {
+ switch (messageType) {
case VmsMessageType.DATA:
handleDataEvent(vec, toByteArray(v.value.bytes));
break;
@@ -378,13 +476,19 @@
case VmsMessageType.UNSUBSCRIBE:
handleUnsubscribeEvent(vec);
break;
+ case VmsMessageType.SUBSCRIBE_TO_PUBLISHER:
+ handleSubscribeToPublisherEvent(vec);
+ break;
+ case VmsMessageType.UNSUBSCRIBE_TO_PUBLISHER:
+ handleUnsubscribeFromPublisherEvent(vec);
+ break;
case VmsMessageType.OFFERING:
handleOfferingEvent(vec);
break;
case VmsMessageType.AVAILABILITY_REQUEST:
- handleAvailabilityEvent();
+ handleHalAvailabilityRequestEvent();
break;
- case VmsMessageType.SUBSCRIPTION_REQUEST:
+ case VmsMessageType.SUBSCRIPTIONS_REQUEST:
handleSubscriptionRequestEvent();
break;
default:
@@ -393,27 +497,42 @@
}
}
+ private VmsLayer parseVmsLayerFromSimpleMessageIntegerValues(List<Integer> integerValues) {
+ return new VmsLayer(integerValues.get(VmsMessageWithLayerIntegerValuesIndex.LAYER_TYPE),
+ integerValues.get(VmsMessageWithLayerIntegerValuesIndex.LAYER_SUBTYPE),
+ integerValues.get(VmsMessageWithLayerIntegerValuesIndex.LAYER_VERSION));
+ }
+
+ private VmsLayer parseVmsLayerFromDataMessageIntegerValues(List<Integer> integerValues) {
+ return parseVmsLayerFromSimpleMessageIntegerValues(integerValues);
+ }
+
+ private int parsePublisherIdFromDataMessageIntegerValues(List<Integer> integerValues) {
+ return integerValues.get(VmsMessageWithLayerAndPublisherIdIntegerValuesIndex.PUBLISHER_ID);
+ }
+
+
/**
* Data message format:
* <ul>
* <li>Message type.
* <li>Layer id.
* <li>Layer version.
+ * <li>Layer subtype.
+ * <li>Publisher ID.
* <li>Payload.
* </ul>
*/
private void handleDataEvent(List<Integer> integerValues, byte[] payload) {
- int layerId = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_ID);
- int layerVersion = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_VERSION);
+ VmsLayer vmsLayer = parseVmsLayerFromDataMessageIntegerValues(integerValues);
+ int publisherId = parsePublisherIdFromDataMessageIntegerValues(integerValues);
if (DBG) {
- Log.d(TAG,
- "Handling a data event for Layer Id: " + layerId +
- " Version: " + layerVersion);
+ Log.d(TAG, "Handling a data event for Layer: " + vmsLayer);
}
// Send the message.
for (VmsHalSubscriberListener listener : mSubscriberListeners) {
- listener.onDataMessage(new VmsLayer(layerId, layerVersion), payload);
+ listener.onDataMessage(vmsLayer, publisherId, payload);
}
}
@@ -423,17 +542,35 @@
* <li>Message type.
* <li>Layer id.
* <li>Layer version.
+ * <li>Layer subtype.
* </ul>
*/
private void handleSubscribeEvent(List<Integer> integerValues) {
- int layerId = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_ID);
- int layerVersion = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_VERSION);
+ VmsLayer vmsLayer = parseVmsLayerFromSimpleMessageIntegerValues(integerValues);
if (DBG) {
- Log.d(TAG,
- "Handling a subscribe event for Layer Id: " + layerId +
- " Version: " + layerVersion);
+ Log.d(TAG, "Handling a subscribe event for Layer: " + vmsLayer);
}
- addHalSubscription(new VmsLayer(layerId, layerVersion));
+ addHalSubscription(vmsLayer);
+ }
+
+ /**
+ * Subscribe message format:
+ * <ul>
+ * <li>Message type.
+ * <li>Layer id.
+ * <li>Layer version.
+ * <li>Layer subtype.
+ * <li>Publisher ID
+ * </ul>
+ */
+ private void handleSubscribeToPublisherEvent(List<Integer> integerValues) {
+ VmsLayer vmsLayer = parseVmsLayerFromSimpleMessageIntegerValues(integerValues);
+ if (DBG) {
+ Log.d(TAG, "Handling a subscribe event for Layer: " + vmsLayer);
+ }
+ int publisherId =
+ integerValues.get(VmsMessageWithLayerAndPublisherIdIntegerValuesIndex.PUBLISHER_ID);
+ addHalSubscriptionToPublisher(vmsLayer, publisherId);
}
/**
@@ -445,42 +582,68 @@
* </ul>
*/
private void handleUnsubscribeEvent(List<Integer> integerValues) {
- int layerId = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_ID);
- int layerVersion = integerValues.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_VERSION);
+ VmsLayer vmsLayer = parseVmsLayerFromSimpleMessageIntegerValues(integerValues);
if (DBG) {
- Log.d(TAG,
- "Handling an unsubscribe event for Layer Id: " + layerId +
- " Version: " + layerVersion);
+ Log.d(TAG, "Handling an unsubscribe event for Layer: " + vmsLayer);
}
- removeHalSubscription(new VmsLayer(layerId, layerVersion));
+ removeHalSubscription(vmsLayer);
+ }
+
+ /**
+ * Unsubscribe message format:
+ * <ul>
+ * <li>Message type.
+ * <li>Layer id.
+ * <li>Layer version.
+ * </ul>
+ */
+ private void handleUnsubscribeFromPublisherEvent(List<Integer> integerValues) {
+ VmsLayer vmsLayer = parseVmsLayerFromSimpleMessageIntegerValues(integerValues);
+ int publisherId =
+ integerValues.get(VmsMessageWithLayerAndPublisherIdIntegerValuesIndex.PUBLISHER_ID);
+ if (DBG) {
+ Log.d(TAG, "Handling an unsubscribe event for Layer: " + vmsLayer);
+ }
+ removeHalSubscriptionFromPublisher(vmsLayer, publisherId);
+ }
+
+ private static int NUM_INTEGERS_IN_VMS_LAYER = 3;
+
+ private VmsLayer parseVmsLayerFromIndex(List<Integer> integerValues, int index) {
+ int layerType = integerValues.get(index++);
+ int layerSutype = integerValues.get(index++);
+ int layerVersion = integerValues.get(index++);
+ return new VmsLayer(layerType, layerSutype, layerVersion);
}
/**
* Offering message format:
* <ul>
* <li>Message type.
+ * <li>Publisher ID.
* <li>Number of offerings.
* <li>Each offering consists of:
- * <ul>
- * <li>Layer id.
- * <li>Layer version.
- * <li>Number of layer dependencies.
- * <li>Layer type/version pairs.
- * </ul>
+ * <ul>
+ * <li>Layer id.
+ * <li>Layer version.
+ * <li>Number of layer dependencies.
+ * <li>Layer type/subtype/version.
+ * </ul>
* </ul>
*/
private void handleOfferingEvent(List<Integer> integerValues) {
+ int publisherId = integerValues.get(VmsOfferingMessageIntegerValuesIndex.PUBLISHER_ID);
int numLayersDependencies =
- integerValues.get(VmsOfferingMessageIntegerValuesIndex.VMS_NUMBER_OF_LAYERS_DEPENDENCIES);
- int idx = VmsOfferingMessageIntegerValuesIndex.FIRST_DEPENDENCIES_INDEX;
+ integerValues.get(
+ VmsOfferingMessageIntegerValuesIndex.NUMBER_OF_OFFERS);
+ int idx = VmsOfferingMessageIntegerValuesIndex.OFFERING_START;
- List<VmsLayerDependency> offeredLayers = new ArrayList<>();
+ Set<VmsLayerDependency> offeredLayers = new HashSet<>();
- // An offering is layerId, LayerVersion, NumDeps, <LayerId, LayerVersion> X NumDeps.
+ // An offering is layerId, LayerVersion, LayerType, NumDeps, <LayerId, LayerVersion> X NumDeps.
for (int i = 0; i < numLayersDependencies; i++) {
- int layerId = integerValues.get(idx++);
- int layerVersion = integerValues.get(idx++);
- VmsLayer offeredLayer = new VmsLayer(layerId, layerVersion);
+ VmsLayer offeredLayer = parseVmsLayerFromIndex(integerValues, idx);
+ idx += NUM_INTEGERS_IN_VMS_LAYER;
int numDependenciesForLayer = integerValues.get(idx++);
if (numDependenciesForLayer == 0) {
@@ -489,19 +652,18 @@
Set<VmsLayer> dependencies = new HashSet<>();
for (int j = 0; j < numDependenciesForLayer; j++) {
- int dependantLayerId = integerValues.get(idx++);
- int dependantLayerVersion = integerValues.get(idx++);
-
- VmsLayer dependantLayer = new VmsLayer(dependantLayerId, dependantLayerVersion);
+ VmsLayer dependantLayer = parseVmsLayerFromIndex(integerValues, idx);
+ idx += NUM_INTEGERS_IN_VMS_LAYER;
dependencies.add(dependantLayer);
}
offeredLayers.add(new VmsLayerDependency(offeredLayer, dependencies));
}
}
// Store the HAL offering.
- VmsLayersOffering offering = new VmsLayersOffering(offeredLayers);
+ VmsLayersOffering offering = new VmsLayersOffering(offeredLayers, publisherId);
synchronized (mLock) {
updateOffering(mHalPublisherToken, offering);
+ VmsOperationRecorder.get().setHalPublisherLayersOffering(offering);
}
}
@@ -510,14 +672,17 @@
* <ul>
* <li>Message type.
* <li>Number of layers.
- * <li>Layer type/version pairs.
+ * <li>Layer type/subtype/version.
* </ul>
*/
- private void handleAvailabilityEvent() {
+ private void handleHalAvailabilityRequestEvent() {
synchronized (mLock) {
- Collection<VmsLayer> availableLayers = mAvailableLayers.getAvailableLayers();
- VehiclePropValue vehiclePropertyValue = toVehiclePropValue(
- VmsMessageType.AVAILABILITY_RESPONSE, availableLayers);
+ Collection<VmsAssociatedLayer> availableLayers = mAvailableLayers.getAvailableLayers();
+ VehiclePropValue vehiclePropertyValue =
+ toAvailabilityUpdateVehiclePropValue(
+ availableLayers,
+ VmsMessageType.AVAILABILITY_RESPONSE);
+
setPropertyValue(vehiclePropertyValue);
}
}
@@ -527,31 +692,38 @@
* <ul>
* <li>Message type.
* </ul>
- *
+ * <p>
* VmsSubscriptionResponseFormat:
* <ul>
* <li>Message type.
* <li>Sequence number.
* <li>Number of layers.
- * <li>Layer type/version pairs.
+ * <li>Layer type/subtype/version.
* </ul>
*/
private void handleSubscriptionRequestEvent() {
VmsSubscriptionState subscription = getSubscriptionState();
- VehiclePropValue vehicleProp = toVehiclePropValue(VmsMessageType.SUBSCRIPTION_RESPONSE);
+ VehiclePropValue vehicleProp =
+ toTypedVmsVehiclePropValue(VmsMessageType.SUBSCRIPTIONS_RESPONSE);
VehiclePropValue.RawValue v = vehicleProp.value;
v.int32Values.add(subscription.getSequenceNumber());
- List<VmsLayer> layers = subscription.getLayers();
+ Set<VmsLayer> layers = subscription.getLayers();
v.int32Values.add(layers.size());
+
+ //TODO(asafro): get the real number of associated layers in the subscriptions
+ // state and send the associated layers themselves.
+ v.int32Values.add(0);
+
for (VmsLayer layer : layers) {
- v.int32Values.add(layer.getId());
+ v.int32Values.add(layer.getType());
+ v.int32Values.add(layer.getSubtype());
v.int32Values.add(layer.getVersion());
}
setPropertyValue(vehicleProp);
}
private void updateOffering(IBinder publisherToken, VmsLayersOffering offering) {
- Set<VmsLayer> availableLayers = Collections.EMPTY_SET;
+ Set<VmsAssociatedLayer> availableLayers = Collections.EMPTY_SET;
synchronized (mLock) {
mOfferings.put(publisherToken, offering);
@@ -560,7 +732,7 @@
availableLayers = mAvailableLayers.getAvailableLayers();
}
- notifySubscribers(availableLayers);
+ notifyOfAvailabilityChange(availableLayers);
}
@Override
@@ -577,22 +749,38 @@
* @return true if the call to the HAL to update the property was successful.
*/
public boolean setSubscriptionRequest(VmsLayer layer, boolean hasSubscribers) {
- VehiclePropValue vehiclePropertyValue = toVehiclePropValue(
- hasSubscribers ? VmsMessageType.SUBSCRIBE : VmsMessageType.UNSUBSCRIBE, layer);
+ VehiclePropValue vehiclePropertyValue = toTypedVmsVehiclePropValueWithLayer(
+ hasSubscribers ? VmsMessageType.SUBSCRIBE : VmsMessageType.UNSUBSCRIBE, layer);
+ return setPropertyValue(vehiclePropertyValue);
+ }
+
+ public boolean setSubscriptionToPublisherRequest(VmsLayer layer,
+ int publisherId,
+ boolean hasSubscribers) {
+ VehiclePropValue vehiclePropertyValue = toTypedVmsVehiclePropValueWithLayer(
+ hasSubscribers ?
+ VmsMessageType.SUBSCRIBE_TO_PUBLISHER :
+ VmsMessageType.UNSUBSCRIBE_TO_PUBLISHER, layer);
+ vehiclePropertyValue.value.int32Values.add(publisherId);
return setPropertyValue(vehiclePropertyValue);
}
public boolean setDataMessage(VmsLayer layer, byte[] payload) {
- VehiclePropValue vehiclePropertyValue = toVehiclePropValue(VmsMessageType.DATA,
- layer,
- payload);
+ VehiclePropValue vehiclePropertyValue =
+ toTypedVmsVehiclePropValueWithLayer(VmsMessageType.DATA, layer);
+ VehiclePropValue.RawValue v = vehiclePropertyValue.value;
+ v.bytes.ensureCapacity(payload.length);
+ for (byte b : payload) {
+ v.bytes.add(b);
+ }
return setPropertyValue(vehiclePropertyValue);
}
- public boolean setAvailableLayers(Collection<VmsLayer> availableLayers) {
+ public boolean notifyAvailabilityChangeToHal(Collection<VmsAssociatedLayer> availableLayers) {
VehiclePropValue vehiclePropertyValue =
- toVehiclePropValue(VmsMessageType.AVAILABILITY_RESPONSE,
- availableLayers);
+ toAvailabilityUpdateVehiclePropValue(
+ availableLayers,
+ VmsMessageType.AVAILABILITY_CHANGE);
return setPropertyValue(vehiclePropertyValue);
}
@@ -607,8 +795,7 @@
return false;
}
- /** Creates a {@link VehiclePropValue} */
- private static VehiclePropValue toVehiclePropValue(int messageType) {
+ private static VehiclePropValue toTypedVmsVehiclePropValue(int messageType) {
VehiclePropValue vehicleProp = new VehiclePropValue();
vehicleProp.prop = HAL_PROPERTY_ID;
vehicleProp.areaId = VehicleAreaType.VEHICLE_AREA_TYPE_NONE;
@@ -618,39 +805,46 @@
return vehicleProp;
}
- /** Creates a {@link VehiclePropValue} */
- private static VehiclePropValue toVehiclePropValue(int messageType, VmsLayer layer) {
- VehiclePropValue vehicleProp = toVehiclePropValue(messageType);
+ /**
+ * Creates a {@link VehiclePropValue}
+ */
+ private static VehiclePropValue toTypedVmsVehiclePropValueWithLayer(
+ int messageType, VmsLayer layer) {
+ VehiclePropValue vehicleProp = toTypedVmsVehiclePropValue(messageType);
VehiclePropValue.RawValue v = vehicleProp.value;
- v.int32Values.add(layer.getId());
+ v.int32Values.add(layer.getType());
+ v.int32Values.add(layer.getSubtype());
v.int32Values.add(layer.getVersion());
return vehicleProp;
}
- /** Creates a {@link VehiclePropValue} with payload */
- private static VehiclePropValue toVehiclePropValue(int messageType,
- VmsLayer layer,
- byte[] payload) {
- VehiclePropValue vehicleProp = toVehiclePropValue(messageType, layer);
- VehiclePropValue.RawValue v = vehicleProp.value;
- v.bytes.ensureCapacity(payload.length);
- for (byte b : payload) {
- v.bytes.add(b);
+ private static VehiclePropValue toAvailabilityUpdateVehiclePropValue(
+ Collection<VmsAssociatedLayer> availableAssociatedLayers, int messageType) {
+
+ if (!AVAILABILITY_MESSAGE_TYPES.contains(messageType)) {
+ throw new IllegalArgumentException("Unsupported availability type: " + messageType);
}
+ VehiclePropValue vehicleProp =
+ toTypedVmsVehiclePropValue(messageType);
+ populateAvailabilityPropValueFields(availableAssociatedLayers, vehicleProp);
return vehicleProp;
+
}
- /** Creates a {@link VehiclePropValue} with payload */
- private static VehiclePropValue toVehiclePropValue(int messageType,
- Collection<VmsLayer> layers) {
- VehiclePropValue vehicleProp = toVehiclePropValue(messageType);
+ private static void populateAvailabilityPropValueFields(
+ Collection<VmsAssociatedLayer> availableAssociatedLayers,
+ VehiclePropValue vehicleProp) {
VehiclePropValue.RawValue v = vehicleProp.value;
- int numLayers = layers.size();
+ int numLayers = availableAssociatedLayers.size();
v.int32Values.add(numLayers);
- for (VmsLayer layer : layers) {
- v.int32Values.add(layer.getId());
- v.int32Values.add(layer.getVersion());
+ for (VmsAssociatedLayer layer : availableAssociatedLayers) {
+ v.int32Values.add(layer.getVmsLayer().getType());
+ v.int32Values.add(layer.getVmsLayer().getSubtype());
+ v.int32Values.add(layer.getVmsLayer().getVersion());
+ v.int32Values.add(layer.getPublisherIds().size());
+ for (int publisherId : layer.getPublisherIds()) {
+ v.int32Values.add(publisherId);
+ }
}
- return vehicleProp;
}
}
diff --git a/tests/CarDiagnosticVerifier/Android.mk b/tests/CarDiagnosticVerifier/Android.mk
new file mode 100644
index 0000000..a006476
--- /dev/null
+++ b/tests/CarDiagnosticVerifier/Android.mk
@@ -0,0 +1,52 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_RESOURCE_DIR := \
+ $(LOCAL_PATH)/res \
+ $(TOP)/frameworks/support/v7/recyclerview/res \
+
+LOCAL_PACKAGE_NAME := CarDiagnosticVerifier
+
+LOCAL_AAPT_FLAGS := --auto-add-overlay \
+ --extra-packages android.support.v7.recyclerview \
+
+LOCAL_JAVA_VERSION := 1.8
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_DEX_PREOPT := false
+
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_STATIC_JAVA_LIBRARIES += \
+ vehicle-hal-support-lib \
+ android-support-v4 \
+ android-support-v7-recyclerview \
+
+LOCAL_JAVA_LIBRARIES += android.car
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/CarDiagnosticVerifier/AndroidManifest.xml b/tests/CarDiagnosticVerifier/AndroidManifest.xml
new file mode 100644
index 0000000..443825a
--- /dev/null
+++ b/tests/CarDiagnosticVerifier/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.car.diagnosticverifier">
+
+ <uses-permission android:name="android.car.permission.DIAGNOSTIC_READ_ALL" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <application android:label="Car Diagnostic Verification">
+ <activity android:name=".MainActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/CarDiagnosticVerifier/res/layout/result_message_item.xml b/tests/CarDiagnosticVerifier/res/layout/result_message_item.xml
new file mode 100644
index 0000000..5c742b6
--- /dev/null
+++ b/tests/CarDiagnosticVerifier/res/layout/result_message_item.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/result_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="16dp"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/CarDiagnosticVerifier/res/layout/verifier_activity.xml b/tests/CarDiagnosticVerifier/res/layout/verifier_activity.xml
new file mode 100644
index 0000000..0c33ed8
--- /dev/null
+++ b/tests/CarDiagnosticVerifier/res/layout/verifier_activity.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp" >
+
+ <TextView
+ android:id="@+id/status_bar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/verification_results"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/status_bar"
+ android:layout_marginTop="16dp" />
+
+</RelativeLayout>
diff --git a/tests/CarDiagnosticVerifier/res/values/strings.xml b/tests/CarDiagnosticVerifier/res/values/strings.xml
new file mode 100644
index 0000000..ca6b33a
--- /dev/null
+++ b/tests/CarDiagnosticVerifier/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <string name="status_receiving">Receiving car diagnostic events...</string>
+ <string name="status_verifying">Verifying car diagnostic events...</string>
+ <string name="status_done">Done with verification</string>
+</resources>
diff --git a/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/DiagnosticJsonConverter.java b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/DiagnosticJsonConverter.java
new file mode 100644
index 0000000..85d4fc8
--- /dev/null
+++ b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/DiagnosticJsonConverter.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.car.diagnosticverifier;
+
+import android.car.diagnostic.CarDiagnosticEvent;
+import android.util.JsonReader;
+
+import com.android.car.vehiclehal.DiagnosticJson;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class provides method to convert JSON into car diagnostic event object.
+ */
+public class DiagnosticJsonConverter {
+
+ public static List<CarDiagnosticEvent> readFromJson(InputStream in) throws IOException {
+ JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
+
+ try {
+ return readEventsArray(reader);
+ } finally {
+ reader.close();
+ }
+ }
+
+ private static List<CarDiagnosticEvent> readEventsArray(JsonReader reader) throws IOException {
+ List<CarDiagnosticEvent> events = new ArrayList<>();
+
+ reader.beginArray();
+ while (reader.hasNext()) {
+ events.add(readEventAndCanonicalize(reader));
+ }
+ reader.endArray();
+ return events;
+ }
+
+ public static CarDiagnosticEvent readEventAndCanonicalize(InputStream in) throws IOException {
+ JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
+ return readEventAndCanonicalize(reader);
+ }
+
+ /**
+ * This method convert JSON to a car diagnostic event object.
+ * Note: it will always set timestamp to 0 and set dtc to null if it is empty string.
+ */
+ private static CarDiagnosticEvent readEventAndCanonicalize(JsonReader reader)
+ throws IOException {
+ DiagnosticJson diagnosticJson = DiagnosticJson.build(reader);
+ //Build event
+ CarDiagnosticEvent.Builder builder = "freeze".equals(diagnosticJson.type) ?
+ CarDiagnosticEvent.Builder.newFreezeFrameBuilder() :
+ CarDiagnosticEvent.Builder.newLiveFrameBuilder();
+ //Always skip timestamp because it is not useful for test
+ builder.atTimestamp(0);
+ for (int i = 0; i < diagnosticJson.intValues.size(); i++) {
+ builder.withIntValue(diagnosticJson.intValues.keyAt(i),
+ diagnosticJson.intValues.valueAt(i));
+ }
+ for (int i = 0; i < diagnosticJson.floatValues.size(); i++) {
+ builder.withFloatValue(diagnosticJson.floatValues.keyAt(i),
+ diagnosticJson.floatValues.valueAt(i));
+ }
+ //Always set dtc to null if it is empty string
+ builder.withDtc("".equals(diagnosticJson.dtc) ? null : diagnosticJson.dtc);
+
+ return builder.build();
+ }
+}
diff --git a/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/DiagnosticVerifier.java b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/DiagnosticVerifier.java
new file mode 100644
index 0000000..1b6e447
--- /dev/null
+++ b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/DiagnosticVerifier.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.car.diagnosticverifier;
+
+import android.car.diagnostic.CarDiagnosticEvent;
+import android.util.JsonWriter;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * DiagVerifier implements verification logic for car diagnostic events.
+ *
+ * The main idea for the verification is similar to a "diff" command on two files, whereas here
+ * is a diff on two event lists. The available diff operations are: "add", "delete", "modify"
+ *
+ * For example, think about doing a diff on two sequences:
+ *
+ * Truth: A B C D E
+ * Received: A C D E F
+ *
+ * The goal is to find the minimal number of diff operations applied on Truth list in order to
+ * become Received list. It is the same problem to find edit distance between two sequences and keep
+ * track of the corresponding edit operations. This verifier applies dynamic programming algorithm
+ * to find the minimal set of diff operations. And the result would be:
+ *
+ * Truth: A - C D E +
+ * Received: A C D E F
+ *
+ * It means in order to become Received list, "B" will be missing and an extra "F" will be added
+ * at the end of list.
+ */
+public class DiagnosticVerifier {
+
+ private static final String TAG = "DiagnosticVerifier";
+ /**
+ * Below are 4 diff operations when comparing two event lists
+ */
+ private static final int DELETE = 0;
+ private static final int ADD = 1;
+ private static final int MODIFY = 2;
+ private static final int KEEP = 3;
+
+ /**
+ * A list of truth diagnostic events for comparison.
+ */
+ private final List<CarDiagnosticEvent> mTruthEventList = new ArrayList<>();
+ /**
+ * A list of received diagnostic events from car service.
+ */
+ private final List<CarDiagnosticEvent> mReceivedEventList = new ArrayList<>();
+
+ /**
+ * Definition of the verification result
+ */
+ static class VerificationResult {
+ public final String testCase;
+ public final boolean success;
+ public final String errorMessage;
+
+ private VerificationResult(String testCase, boolean success, String errorMessage) {
+ this.testCase = testCase;
+ this.success = success;
+ this.errorMessage = errorMessage;
+ }
+
+ public static VerificationResult fromMessage(String testCase, String message) {
+ return new VerificationResult(testCase, message.length() == 0, message);
+ }
+
+ public void writeToJson(JsonWriter jsonWriter) throws IOException {
+ jsonWriter.beginObject();
+
+ jsonWriter.name("testCase");
+ jsonWriter.value(this.testCase);
+
+ jsonWriter.name("success");
+ jsonWriter.value(this.success);
+
+ jsonWriter.name("errorMessage");
+ jsonWriter.value(this.errorMessage);
+
+ jsonWriter.endObject();
+ }
+ }
+
+ public DiagnosticVerifier(List<CarDiagnosticEvent> truthEvents) {
+ if (truthEvents != null) {
+ for (CarDiagnosticEvent event : truthEvents) {
+ CarDiagnosticEvent canonicalEvent = canonicalize(event);
+ mTruthEventList.add(canonicalEvent);
+ }
+ }
+ }
+
+ public void receiveEvent(CarDiagnosticEvent event) {
+ CarDiagnosticEvent newEvent = canonicalize(event);
+ mReceivedEventList.add(newEvent);
+ }
+
+ public List<VerificationResult> verify() {
+ List<Integer> diff = calculateDiffOperations();
+ StringBuilder missingEventMsgBuilder = new StringBuilder();
+ StringBuilder extraEventMsgBuilder = new StringBuilder();
+ StringBuilder mismatchEventMsgBuilder = new StringBuilder();
+ for (int i = 0, j = 0, k = diff.size() - 1; k >= 0; k--) {
+ if (diff.get(k) == DELETE) {
+ missingEventMsgBuilder.append(String.format(
+ "Missing event at position %d: %s\n", i, mTruthEventList.get(i)));
+ i++;
+ } else if (diff.get(k) == ADD) {
+ extraEventMsgBuilder.append(String.format(
+ "Extra event at position %d: %s\n", i, mReceivedEventList.get(j)));
+ j++;
+ } else if (diff.get(k) == MODIFY) {
+ mismatchEventMsgBuilder.append(String.format(
+ "Mismatched event pair at position %d:\n" +
+ "True event -- %s\nWrong event -- %s\n",
+ i, mTruthEventList.get(i), mReceivedEventList.get(j)));
+ i++;
+ j++;
+ } else {
+ i++;
+ j++;
+ }
+ }
+ List<VerificationResult> results = new ArrayList<>();
+ results.add(VerificationResult.fromMessage(
+ "test_mismatched_event", mismatchEventMsgBuilder.toString()));
+ results.add(VerificationResult.fromMessage(
+ "test_missing_event", missingEventMsgBuilder.toString()));
+ results.add(VerificationResult.fromMessage(
+ "test_extra_event", extraEventMsgBuilder.toString()));
+ return results;
+ }
+
+ /**
+ * The function applies a dynamic programming algorithm to find the minimal set of diff
+ * operations that applied on truth event list in order to become received event list
+ */
+ private List<Integer> calculateDiffOperations() {
+ final int n = mTruthEventList.size();
+ final int m = mReceivedEventList.size();
+
+ int[][] diffTable = new int[n + 1][m + 1];
+ int[][] costTable = new int[n + 1][m + 1];
+
+ for (int i = 1; i <= n; i++) {
+ costTable[i][0] = i;
+ diffTable[i][0] = DELETE;
+ }
+
+ for (int i = 1; i <= m; i++) {
+ costTable[0][i] = i;
+ diffTable[0][i] = ADD;
+ }
+
+ for (int i = 1; i <= n; i++) {
+ for (int j = 1; j <= m; j++) {
+ int deleteCost = costTable[i - 1][j] + 1;
+ int addCost = costTable[i][j - 1] + 1;
+ int modifyCost = costTable[i - 1][j - 1];
+
+ CarDiagnosticEvent trueEvent = mTruthEventList.get(i - 1);
+ CarDiagnosticEvent receivedEvent = mReceivedEventList.get(j - 1);
+
+ //TODO: Use a more meaningful comparison. Instead of strict object level equality,
+ //can check logical equality and allow an acceptable difference.
+ boolean isEqual = trueEvent.equals(receivedEvent);
+ modifyCost += isEqual ? 0 : 1;
+
+ int minCost = modifyCost;
+ int move = isEqual ? KEEP : MODIFY;
+ if (minCost > addCost) {
+ minCost = addCost;
+ move = ADD;
+ }
+ if (minCost > deleteCost) {
+ minCost = deleteCost;
+ move = DELETE;
+ }
+
+ costTable[i][j] = minCost;
+ diffTable[i][j] = move;
+ }
+ }
+ List<Integer> diff = new ArrayList<>();
+
+ for (int i = n, j = m; i > 0 || j > 0; ) {
+ diff.add(diffTable[i][j]);
+ if (diffTable[i][j] == DELETE) {
+ i--;
+ } else if (diffTable[i][j] == ADD) {
+ j--;
+ } else {
+ i--;
+ j--;
+ }
+ }
+ return diff;
+ }
+
+ /**
+ * The function will canonicalize a given event by using JSON converter which will reset event
+ * timestamp to 0 and set DTC field with empty string to null. Doing JSON conversion is because
+ * CarDiagnosticEvent does not provide direct accessor for intValues and floatValues.
+ */
+ private CarDiagnosticEvent canonicalize(CarDiagnosticEvent event) {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ JsonWriter writer = new JsonWriter(new OutputStreamWriter(out));
+ CarDiagnosticEvent newEvent = event;
+ try {
+ event.writeToJson(writer);
+ writer.flush();
+ writer.close();
+ byte[] rawJson = out.toByteArray();
+ ByteArrayInputStream in = new ByteArrayInputStream(rawJson);
+ newEvent = DiagnosticJsonConverter.readEventAndCanonicalize(in);
+ in.close();
+ out.close();
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to clear timestamp ");
+ }
+ return newEvent;
+ }
+}
diff --git a/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/MainActivity.java b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/MainActivity.java
new file mode 100644
index 0000000..75db2ed
--- /dev/null
+++ b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/MainActivity.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.car.diagnosticverifier;
+
+import android.app.Activity;
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.diagnostic.CarDiagnosticEvent;
+import android.car.diagnostic.CarDiagnosticManager;
+import android.car.hardware.CarSensorManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.IBinder;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.JsonWriter;
+import android.util.Log;
+import android.widget.TextView;
+
+import com.google.android.car.diagnosticverifier.DiagnosticVerifier.VerificationResult;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The test app that does the verification of car diagnostic event data. It first reads the
+ * truth (golden) event data from a JSON file upon starting. Then a broadcast intent such as:
+ *
+ * am broadcast -a com.google.android.car.diagnosticverifier.action.START_LISTEN
+ *
+ * will activate the car diagnostics listener. The test app will receive events from diagnostic API.
+ * Once it receives all the events, a broadcast intent with "stop" action such as:
+ *
+ * am broadcast -a com.google.android.car.diagnosticverifier.action.STOP_LISTEN
+ *
+ * will deactivate the listener and start the verification process (see {@link DiagnosticVerifier}).
+ *
+ * Verification result will be output to a JSON file on device.
+ */
+public class MainActivity extends Activity {
+ public static final String TAG = "DiagnosticVerifier";
+
+ public static final String ACTION_START_LISTEN =
+ "com.google.android.car.diagnosticverifier.action.START_LISTEN";
+ public static final String ACTION_STOP_LISTEN =
+ "com.google.android.car.diagnosticverifier.action.STOP_LISTEN";
+
+ private static final String DEFAULT_JSON_PATH = "/data/local/tmp/diag.json";
+
+ private static final String JSON_PATH_KEY = "jsonPath";
+ private static final String JSON_RESULT = "verification_result.json";
+
+ private Car mCar;
+ private CarDiagnosticManager mCarDiagnosticManager;
+ private DiagnosticListener mDiagnosticListener;
+ private BroadcastReceiver mBroadcastReceiver;
+ private DiagnosticVerifier mVerifier;
+ private TextView mStatusBar;
+ private RecyclerView mRecyclerView;
+ private VerificationResultAdapter mResultAdapter;
+ private boolean mListening = false;
+
+ private final ServiceConnection mCarConnectionListener =
+ new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder iBinder) {
+ Log.d(TAG, "Connected to " + name.flattenToString());
+ try {
+ mCarDiagnosticManager =
+ (CarDiagnosticManager) mCar.getCarManager(Car.DIAGNOSTIC_SERVICE);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Failed to get a connection", e);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.d(TAG, "Disconnected from " + name.flattenToString());
+
+ mCar = null;
+ mCarDiagnosticManager = null;
+ }
+ };
+
+ class DiagnosticListener implements CarDiagnosticManager.OnDiagnosticEventListener {
+
+ @Override
+ public void onDiagnosticEvent(CarDiagnosticEvent carDiagnosticEvent) {
+ Log.v(TAG, "Received Car Diagnostic Event: " + carDiagnosticEvent.toString());
+ mVerifier.receiveEvent(carDiagnosticEvent);
+ }
+ }
+
+ class VerifierMsgReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ Log.d(TAG, "Received intent with action: " + action);
+ if (ACTION_START_LISTEN.equals(action)) {
+ try {
+ startListen();
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Failed to listen for car diagnostic event", e);
+ }
+ } else if (ACTION_STOP_LISTEN.equals(action)) {
+ stopListen();
+ verify();
+ }
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.verifier_activity);
+
+ mStatusBar = (TextView) findViewById(R.id.status_bar);
+
+ //Setting up RecyclerView to show verification result messages
+ mRecyclerView = (RecyclerView) findViewById(R.id.verification_results);
+ LinearLayoutManager layoutManager =
+ new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
+ mRecyclerView.setLayoutManager(layoutManager);
+ mResultAdapter = new VerificationResultAdapter();
+ mRecyclerView.setAdapter(mResultAdapter);
+
+ //Connect to car service
+ mCar = Car.createCar(this, mCarConnectionListener);
+ mCar.connect();
+
+ //Initialize broadcast intent receiver
+ mBroadcastReceiver = new VerifierMsgReceiver();
+ IntentFilter filter = new IntentFilter(ACTION_START_LISTEN);
+ filter.addAction(ACTION_STOP_LISTEN);
+ this.registerReceiver(mBroadcastReceiver, filter);
+
+ //Read golden diagnostics JSON file
+ String jsonPath = this.getIntent().getStringExtra(JSON_PATH_KEY);
+ if (jsonPath == null || jsonPath.isEmpty()) {
+ jsonPath = DEFAULT_JSON_PATH;
+ }
+ List<CarDiagnosticEvent> events;
+ try {
+ events = DiagnosticJsonConverter.readFromJson(new FileInputStream(jsonPath));
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to read diagnostic JSON file", e);
+ }
+ Log.d(TAG, String.format("Read %d events from JSON file %s.", events.size(), jsonPath));
+
+ mVerifier = new DiagnosticVerifier(events);
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (mCar != null) {
+ mCar.disconnect();
+ }
+ mVerifier = null;
+ this.unregisterReceiver(mBroadcastReceiver);
+ }
+
+ private void startListen() throws CarNotConnectedException {
+ if (mListening) {
+ return;
+ }
+ if (mDiagnosticListener == null) {
+ mDiagnosticListener = new DiagnosticListener();
+ }
+ Log.i(TAG, "Start listening for car diagnostics events");
+ mCarDiagnosticManager.registerListener(
+ mDiagnosticListener,
+ CarDiagnosticManager.FRAME_TYPE_LIVE,
+ CarSensorManager.SENSOR_RATE_NORMAL);
+ mCarDiagnosticManager.registerListener(
+ mDiagnosticListener,
+ CarDiagnosticManager.FRAME_TYPE_FREEZE,
+ CarSensorManager.SENSOR_RATE_NORMAL);
+
+ mListening = true;
+ mStatusBar.setText(R.string.status_receiving);
+ }
+
+ private void stopListen() {
+ Log.i(TAG, "Stop listening for car diagnostics events");
+ mCarDiagnosticManager.unregisterListener(mDiagnosticListener);
+ mListening = false;
+ }
+
+ private boolean isExternalStorageWritable() {
+ String state = Environment.getExternalStorageState();
+ return Environment.MEDIA_MOUNTED.equals(state);
+ }
+
+ private File getResultJsonFile() throws IOException {
+ if (!isExternalStorageWritable()) {
+ throw new IOException("External storage is not writable. Cannot save content");
+ }
+
+ File resultJson = new File(Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOCUMENTS), JSON_RESULT);
+ if (!resultJson.getParentFile().mkdirs()) {
+ Log.w(TAG, "Parent directory may already exist");
+ }
+ return resultJson;
+ }
+
+ private void verify() {
+ Log.d(TAG, "Start verifying car diagnostics events");
+ mStatusBar.setText(R.string.status_verifying);
+ List<VerificationResult> results = mVerifier.verify();
+ mStatusBar.setText(R.string.status_done);
+
+ if (results.isEmpty()) {
+ Log.d(TAG, "Verification result is empty.");
+ return;
+ }
+
+ List<String> resultMessages = new ArrayList<>();
+ try {
+ File resultJson = getResultJsonFile();
+ JsonWriter writer = new JsonWriter(
+ new OutputStreamWriter(new FileOutputStream(resultJson)));
+
+ writer.beginArray();
+ for (VerificationResult result : results) {
+ resultMessages.add("Test case: " + result.testCase);
+ resultMessages.add("Result: " + result.success);
+ resultMessages.add(result.errorMessage);
+ result.writeToJson(writer);
+ }
+ writer.endArray();
+ writer.flush();
+ writer.close();
+ Log.i(TAG, "Verification result: " + resultJson.getAbsolutePath());
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to save verification result.", e);
+ }
+ mResultAdapter.setResultMessages(resultMessages);
+ }
+}
+
diff --git a/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/VerificationResultAdapter.java b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/VerificationResultAdapter.java
new file mode 100644
index 0000000..e6453b6
--- /dev/null
+++ b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/VerificationResultAdapter.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.car.diagnosticverifier;
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.List;
+
+/**
+ * A recycler view adapter for verification result messages
+ */
+public class VerificationResultAdapter extends
+ RecyclerView.Adapter<VerificationResultAdapter.VerificationResultViewHolder> {
+
+ private List<String> mResultMessages;
+
+ @Override
+ public VerificationResultViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
+ Context context = viewGroup.getContext();
+ int messageItemLayoutId = R.layout.result_message_item;
+ LayoutInflater inflater = LayoutInflater.from(context);
+ boolean shouldAttachToParentImmediately = false;
+
+ View view = inflater.inflate(
+ messageItemLayoutId, viewGroup, shouldAttachToParentImmediately);
+ return new VerificationResultViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(VerificationResultViewHolder verificationResultViewHolder, int i) {
+ String resultMessage = mResultMessages.get(i);
+ verificationResultViewHolder.mResultMessageTextView.setText(resultMessage);
+ }
+
+ @Override
+ public int getItemCount() {
+ if (mResultMessages == null) {
+ return 0;
+ }
+ return mResultMessages.size();
+ }
+
+ public void setResultMessages(List<String> resultMessages) {
+ mResultMessages = resultMessages;
+ notifyDataSetChanged();
+ }
+
+ public class VerificationResultViewHolder extends RecyclerView.ViewHolder {
+ public final TextView mResultMessageTextView;
+
+ public VerificationResultViewHolder(View view) {
+ super(view);
+ mResultMessageTextView = (TextView) view.findViewById(R.id.result_message);
+ }
+ }
+}
diff --git a/tests/DirectRenderingClusterSample/Android.mk b/tests/DirectRenderingClusterSample/Android.mk
new file mode 100644
index 0000000..7fb6e8e
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/Android.mk
@@ -0,0 +1,35 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := DirectRenderingClusterSample
+
+# Each update should be signed by OEMs
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_JAVA_LIBRARIES += android.car
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4
+
+include $(BUILD_PACKAGE)
diff --git a/tests/DirectRenderingClusterSample/AndroidManifest.xml b/tests/DirectRenderingClusterSample/AndroidManifest.xml
new file mode 100644
index 0000000..fc41ef0
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/AndroidManifest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="android.car.cluster.sample"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-sdk android:targetSdkVersion="25" android:minSdkVersion="25"/>
+
+ <!-- We set TYPE_SYSTEM_ALERT window flag to presentation in order
+ to show it outside of activity context -->
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
+ <uses-permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"
+ tools:ignore="ProtectedPermissions"/>
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"/>
+ <uses-permission android:name="android.permission.INJECT_EVENTS"/>
+
+ <application android:label="@string/app_name"
+ android:icon="@mipmap/ic_launcher"
+ android:directBootAware="true"
+ android:persistent="true">
+ <service android:name=".SampleClusterServiceImpl"
+ android:exported="false"
+ android:permission="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"/>
+
+ <activity android:name=".MainClusterActivity"
+ android:exported="false"
+ android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+
+ </application>
+</manifest>
diff --git a/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_car_info.png b/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_car_info.png
new file mode 100644
index 0000000..adb07d6
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_car_info.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_music.png b/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_music.png
new file mode 100644
index 0000000..f1b2533
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_music.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_nav.png b/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_nav.png
new file mode 100644
index 0000000..8036b89
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_nav.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_phone.png b/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_phone.png
new file mode 100644
index 0000000..2daaa8e
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-hdpi/ic_phone.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_car_info.png b/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_car_info.png
new file mode 100644
index 0000000..9c45d3e
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_car_info.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_music.png b/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_music.png
new file mode 100644
index 0000000..fb0671a
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_music.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_nav.png b/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_nav.png
new file mode 100644
index 0000000..4844c89
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_nav.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_phone.png b/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_phone.png
new file mode 100644
index 0000000..ef50db6
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-mdpi/ic_phone.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_car_info.png b/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_car_info.png
new file mode 100644
index 0000000..2db5645
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_car_info.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_music.png b/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_music.png
new file mode 100644
index 0000000..6154079
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_music.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_nav.png b/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_nav.png
new file mode 100644
index 0000000..f94db1e
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_nav.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_phone.png b/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_phone.png
new file mode 100644
index 0000000..ca0be39
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-xhdpi/ic_phone.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_car_info.png b/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_car_info.png
new file mode 100644
index 0000000..f00be30
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_car_info.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_music.png b/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_music.png
new file mode 100644
index 0000000..8a875ad
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_music.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_nav.png b/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_nav.png
new file mode 100644
index 0000000..7b2b514
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_nav.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_phone.png b/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_phone.png
new file mode 100644
index 0000000..fce6fb7
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable-xxhdpi/ic_phone.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable/btn_car_info.xml b/tests/DirectRenderingClusterSample/res/drawable/btn_car_info.xml
new file mode 100644
index 0000000..437ad41
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable/btn_car_info.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true" >
+ <layer-list>
+ <item android:drawable="@drawable/ic_car_info"/>
+ <item android:drawable="@drawable/focused_button_shape"/>
+ </layer-list>
+ </item>
+ <item android:drawable="@drawable/ic_car_info"/>
+</selector>
\ No newline at end of file
diff --git a/tests/DirectRenderingClusterSample/res/drawable/btn_music.xml b/tests/DirectRenderingClusterSample/res/drawable/btn_music.xml
new file mode 100644
index 0000000..65e01b6
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable/btn_music.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true" >
+ <layer-list>
+ <item android:drawable="@drawable/ic_music"/>
+ <item android:drawable="@drawable/focused_button_shape"/>
+ </layer-list>
+ </item>
+ <item android:drawable="@drawable/ic_music"/>
+</selector>
\ No newline at end of file
diff --git a/tests/DirectRenderingClusterSample/res/drawable/btn_nav.xml b/tests/DirectRenderingClusterSample/res/drawable/btn_nav.xml
new file mode 100644
index 0000000..9f9c6bc
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable/btn_nav.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true" >
+ <layer-list>
+ <item android:drawable="@drawable/ic_nav"/>
+ <item android:drawable="@drawable/focused_button_shape"/>
+ </layer-list>
+ </item>
+ <item android:drawable="@drawable/ic_nav"/>
+</selector>
\ No newline at end of file
diff --git a/tests/DirectRenderingClusterSample/res/drawable/btn_phone.xml b/tests/DirectRenderingClusterSample/res/drawable/btn_phone.xml
new file mode 100644
index 0000000..2a6e249
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable/btn_phone.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true" >
+ <layer-list>
+ <item android:drawable="@drawable/ic_phone"/>
+ <item android:drawable="@drawable/focused_button_shape"/>
+ </layer-list>
+ </item>
+ <item android:drawable="@drawable/ic_phone"/>
+</selector>
\ No newline at end of file
diff --git a/tests/DirectRenderingClusterSample/res/drawable/car_top_view.png b/tests/DirectRenderingClusterSample/res/drawable/car_top_view.png
new file mode 100644
index 0000000..b19ee12
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable/car_top_view.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/drawable/focused_button_shape.xml b/tests/DirectRenderingClusterSample/res/drawable/focused_button_shape.xml
new file mode 100644
index 0000000..b84ef30
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/drawable/focused_button_shape.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <stroke
+ android:width="2dp"
+ android:color="#333333" />
+ <corners
+ android:radius="7dp" />
+ <padding
+ android:left="5dp"
+ android:top="5dp"
+ android:right="5dp"
+ android:bottom="5dp" />
+</shape>
\ No newline at end of file
diff --git a/tests/DirectRenderingClusterSample/res/layout/activity_main.xml b/tests/DirectRenderingClusterSample/res/layout/activity_main.xml
new file mode 100644
index 0000000..7e1ef9f
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/layout/activity_main.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/activity_main"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/background_dark"
+ tools:context=".MainClusterActivity"
+ android:windowIsFloating="true">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <android.support.v4.view.ViewPager
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/pager"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ />
+
+ <LinearLayout
+ android:layout_gravity="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/btn_nav"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:background="@drawable/btn_nav"
+ android:layout_margin="10dp"
+ android:focusableInTouchMode="true" />
+ <Button
+ android:id="@+id/btn_phone"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_margin="10dp"
+ android:background="@drawable/btn_phone"
+ android:focusableInTouchMode="true" />
+ <Button
+ android:id="@+id/btn_music"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_margin="10dp"
+ android:background="@drawable/btn_music"
+ android:focusableInTouchMode="true" />
+ <Button
+ android:id="@+id/btn_car_info"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_margin="10dp"
+ android:background="@drawable/btn_car_info"
+ android:focusableInTouchMode="true" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/text_overlay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:background="@android:color/background_light"
+ android:textSize="30sp"
+ />
+</RelativeLayout>
diff --git a/tests/DirectRenderingClusterSample/res/layout/fragment_car_info.xml b/tests/DirectRenderingClusterSample/res/layout/fragment_car_info.xml
new file mode 100644
index 0000000..3a1f5ab
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/layout/fragment_car_info.xml
@@ -0,0 +1,46 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="com.google.experiments.client.pavelm.fakeclusterux.CarInfoFragment">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="32 psi"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginLeft="-80dp"
+ />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="33 psi"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginLeft="80dp"
+ />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="33 psi"
+ android:layout_gravity="center_horizontal|bottom"
+ android:layout_marginLeft="80dp"
+ />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="31 psi"
+ android:layout_gravity="center_horizontal|bottom"
+ android:layout_marginLeft="-80dp"
+ />
+
+ <ImageView
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:src="@drawable/car_top_view"
+ android:padding="30dp"
+ android:scaleType="fitCenter" />
+
+</FrameLayout>
diff --git a/tests/DirectRenderingClusterSample/res/layout/fragment_music.xml b/tests/DirectRenderingClusterSample/res/layout/fragment_music.xml
new file mode 100644
index 0000000..1a11b3f
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/layout/fragment_music.xml
@@ -0,0 +1,14 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="com.google.experiments.client.pavelm.fakeclusterux.MusicFragment"
+ android:background="@color/colorPrimaryDark">
+
+ <!-- TODO: Update blank fragment layout -->
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="@string/hello_blank_fragment" />
+
+</FrameLayout>
diff --git a/tests/DirectRenderingClusterSample/res/layout/fragment_navigation.xml b/tests/DirectRenderingClusterSample/res/layout/fragment_navigation.xml
new file mode 100644
index 0000000..c0fb4b3
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/layout/fragment_navigation.xml
@@ -0,0 +1,22 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/darkBlue"
+ tools:context=".NavigationFragment">
+
+ <FrameLayout
+ android:id="@+id/nav_frame_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <SurfaceView
+ android:id="@+id/nav_surface"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="20dp"/>
+
+ </FrameLayout>
+
+
+</FrameLayout>
diff --git a/tests/DirectRenderingClusterSample/res/layout/fragment_phone.xml b/tests/DirectRenderingClusterSample/res/layout/fragment_phone.xml
new file mode 100644
index 0000000..42fe24a
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/layout/fragment_phone.xml
@@ -0,0 +1,13 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="com.google.experiments.client.pavelm.fakeclusterux.PhoneFragment">
+
+ <!-- TODO: Update blank fragment layout -->
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="@string/hello_blank_fragment" />
+
+</FrameLayout>
diff --git a/tests/DirectRenderingClusterSample/res/mipmap-hdpi/ic_launcher.png b/tests/DirectRenderingClusterSample/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/mipmap-mdpi/ic_launcher.png b/tests/DirectRenderingClusterSample/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/mipmap-xhdpi/ic_launcher.png b/tests/DirectRenderingClusterSample/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/mipmap-xxhdpi/ic_launcher.png b/tests/DirectRenderingClusterSample/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/mipmap-xxxhdpi/ic_launcher.png b/tests/DirectRenderingClusterSample/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/DirectRenderingClusterSample/res/values-w820dp/dimens.xml b/tests/DirectRenderingClusterSample/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+<resources>
+ <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+ (such as screen margins) for screens with more than 820dp of available width. This
+ would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+ <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/tests/DirectRenderingClusterSample/res/values/colors.xml b/tests/DirectRenderingClusterSample/res/values/colors.xml
new file mode 100644
index 0000000..a71c0d5
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/values/colors.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="colorPrimary">#3F51B5</color>
+ <color name="colorPrimaryDark">#303F9F</color>
+ <color name="colorAccent">#FF4081</color>
+ <color name="darkBlue">#2b2b77</color>
+</resources>
diff --git a/tests/DirectRenderingClusterSample/res/values/dimens.xml b/tests/DirectRenderingClusterSample/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/values/dimens.xml
@@ -0,0 +1,5 @@
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/tests/DirectRenderingClusterSample/res/values/strings.xml b/tests/DirectRenderingClusterSample/res/values/strings.xml
new file mode 100644
index 0000000..778ecf6
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/values/strings.xml
@@ -0,0 +1,6 @@
+<resources>
+ <string name="app_name">Fake Cluster Implementation</string>
+
+ <!-- TODO: Remove or change this placeholder text -->
+ <string name="hello_blank_fragment">Hello blank fragment</string>
+</resources>
diff --git a/tests/DirectRenderingClusterSample/res/values/styles.xml b/tests/DirectRenderingClusterSample/res/values/styles.xml
new file mode 100644
index 0000000..f11f745
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/res/values/styles.xml
@@ -0,0 +1,3 @@
+<resources>
+
+</resources>
diff --git a/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/CarInfoFragment.java b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/CarInfoFragment.java
new file mode 100644
index 0000000..d1e7112
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/CarInfoFragment.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster.sample;
+
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+
+/**
+ * A simple {@link Fragment} subclass.
+ */
+public class CarInfoFragment extends Fragment {
+
+
+ public CarInfoFragment() {
+ // Required empty public constructor
+ }
+
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ return inflater.inflate(R.layout.fragment_car_info, container, false);
+ }
+
+}
diff --git a/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/MainClusterActivity.java b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/MainClusterActivity.java
new file mode 100644
index 0000000..1c3f7af
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/MainClusterActivity.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster.sample;
+
+import static android.car.cluster.sample.SampleClusterServiceImpl.LOCAL_BINDING_ACTION;
+
+import android.car.cluster.sample.SampleClusterServiceImpl.Listener;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+
+public class MainClusterActivity extends FragmentActivity
+ implements Listener {
+ private static final String TAG = MainClusterActivity.class.getSimpleName();
+
+ private Button mNavButton;
+ private Button mPhoneButton;
+ private Button mCarInfoButton;
+ private Button mMusicButton;
+ private TextView mTextOverlay;
+ private ViewPager mPager;
+
+ private SampleClusterServiceImpl mService;
+
+ private final Handler mHandler = new Handler();
+
+ private HashMap<Button, Facet<?>> mButtonToFacet = new HashMap<>();
+ private SparseArray<Facet<?>> mOrderToFacet = new SparseArray<>();
+
+ private final View.OnFocusChangeListener mFacetButtonFocusListener =
+ new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (hasFocus) {
+ mPager.setCurrentItem(mButtonToFacet.get(v).order);
+ }
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ Intent intent = new Intent(this, SampleClusterServiceImpl.class);
+ intent.setAction(LOCAL_BINDING_ACTION);
+ bindService(intent,
+ new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.i(TAG, "onServiceConnected, name: " + name + ", service: " + service);
+ mService = ((SampleClusterServiceImpl.LocalBinder) service)
+ .getService();
+ mService.registerListener(MainClusterActivity.this);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.i(TAG, "onServiceDisconnected, name: " + name);
+ mService = null;
+ }
+ }, BIND_AUTO_CREATE);
+
+ mNavButton = findViewById(R.id.btn_nav);
+ mPhoneButton = findViewById(R.id.btn_phone);
+ mCarInfoButton = findViewById(R.id.btn_car_info);
+ mMusicButton = findViewById(R.id.btn_music);
+ mTextOverlay = findViewById(R.id.text_overlay);
+
+ registerFacets(
+ new Facet<>(mNavButton, 0, NavigationFragment.class),
+ new Facet<>(mPhoneButton, 1, PhoneFragment.class),
+ new Facet<>(mMusicButton, 2, MusicFragment.class),
+ new Facet<>(mCarInfoButton, 3, CarInfoFragment.class));
+
+ mPager = (ViewPager) findViewById(R.id.pager);
+ mPager.setAdapter(new ClusterPageAdapter(getSupportFragmentManager()));
+
+ mNavButton.requestFocus();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mService != null) {
+ mService.unregisterListener();
+ }
+ }
+
+ @Override
+ public void onShowToast(String text) {
+ if (mTextOverlay.getVisibility() == View.VISIBLE) {
+ if (!TextUtils.isEmpty(mTextOverlay.getText())) {
+ mTextOverlay.setText(mTextOverlay.getText() + "\n" + text);
+ } else {
+ mTextOverlay.setText(text);
+ }
+ }
+
+ mTextOverlay.setVisibility(View.VISIBLE);
+
+ mHandler.removeCallbacksAndMessages(null);
+ mHandler.postDelayed(() -> {
+ mTextOverlay.setVisibility(View.GONE);
+ mTextOverlay.setText("");
+ }, 3000);
+ }
+
+ @Override
+ public void onKeyEvent(KeyEvent event) {
+ Log.i(TAG, "onKeyEvent, event: " + event);
+ dispatchKeyEvent(event); // TODO: dispatch event doesn't work for some reason.
+
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) {
+ int nextItem = (mPager.getCurrentItem() + 1) % mButtonToFacet.size();
+ mOrderToFacet.get(nextItem).button.requestFocus();
+ } else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) {
+ int nextItem = (mPager.getCurrentItem() - 1);
+ if (nextItem < 0) nextItem = mButtonToFacet.size() - 1;
+ mOrderToFacet.get(nextItem).button.requestFocus();
+ }
+ }
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ boolean consumed = super.dispatchKeyEvent(event);
+ Log.i(TAG, "dispatchKeyEvent, event: " + event + ", consumed: " + consumed);
+ return consumed;
+ }
+
+ public class ClusterPageAdapter extends FragmentPagerAdapter {
+ public ClusterPageAdapter(FragmentManager fm) {
+ super(fm);
+ }
+
+ @Override
+ public int getCount() {
+ return mButtonToFacet.size();
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ return mOrderToFacet.get(position).getOrCreateFragment();
+ }
+ }
+
+ private void registerFacets(Facet<?>... facets) {
+ for (Facet<?> f : facets) {
+ registerFacet(f);
+ }
+ }
+
+ private <T> void registerFacet(Facet<T> facet) {
+ mOrderToFacet.append(facet.order, facet);
+ mButtonToFacet.put(facet.button, facet);
+
+ facet.button.setOnFocusChangeListener(mFacetButtonFocusListener);
+ }
+
+ private static class Facet<T> {
+ Button button;
+ Class<T> clazz;
+ int order;
+
+ Facet(Button button, int order, Class<T> clazz) {
+ this.button = button;
+ this.order = order;
+ this.clazz = clazz;
+ }
+
+ private Fragment mFragment;
+
+ Fragment getOrCreateFragment() {
+ if (mFragment == null) {
+ try {
+ mFragment = (Fragment) clazz.getConstructors()[0].newInstance();
+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return mFragment;
+ }
+ }
+
+ SampleClusterServiceImpl getService() {
+ return mService;
+ }
+}
diff --git a/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/MusicFragment.java b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/MusicFragment.java
new file mode 100644
index 0000000..2c82065
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/MusicFragment.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster.sample;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A simple {@link Fragment} subclass.
+ */
+public class MusicFragment extends Fragment {
+
+ public MusicFragment() {
+ // Required empty public constructor
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ return inflater.inflate(R.layout.fragment_music, container, false);
+ }
+}
diff --git a/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/NavigationFragment.java b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/NavigationFragment.java
new file mode 100644
index 0000000..0ffac1e
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/NavigationFragment.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster.sample;
+
+import static android.car.cluster.CarInstrumentClusterManager.CATEGORY_NAVIGATION;
+
+import android.app.ActivityOptions;
+import android.car.CarNotConnectedException;
+import android.car.cluster.ClusterActivityState;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.hardware.display.VirtualDisplay;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceHolder.Callback;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class NavigationFragment extends Fragment {
+ private final static String TAG = "Cluster.NavigationFragment";
+
+ private SurfaceView mSurfaceView;
+ private DisplayManager mDisplayManager;
+ private Rect mUnobscuredBounds;
+
+ // Static because we want to keep alive this virtual display when navigating through
+ // ViewPager (this fragment gets dynamically destroyed and created)
+ private static VirtualDisplay mVirtualDisplay;
+ private static int mRegisteredNavDisplayId = Display.INVALID_DISPLAY;
+
+ public NavigationFragment() {
+ // Required empty public constructor
+ }
+
+ private final DisplayListener mDisplayListener = new DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ int navDisplayId = getVirtualDisplayId();
+ Log.i(TAG, "onDisplayAdded, displayId: " + displayId
+ + ", navigation display id: " + navDisplayId);
+
+ if (navDisplayId == displayId) {
+ try {
+ getService().setClusterActivityLaunchOptions(
+ CATEGORY_NAVIGATION,
+ ActivityOptions.makeBasic()
+ .setLaunchDisplayId(displayId));
+ mRegisteredNavDisplayId = displayId;
+
+ getService().setClusterActivityState(
+ CATEGORY_NAVIGATION,
+ ClusterActivityState.create(true, mUnobscuredBounds).toBundle());
+ } catch (CarNotConnectedException e) {
+ throw new IllegalStateException(
+ "Failed to report nav activity cluster launch options", e);
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ if (mRegisteredNavDisplayId == displayId) {
+ try {
+ mRegisteredNavDisplayId = Display.INVALID_DISPLAY;
+ getService().setClusterActivityLaunchOptions(
+ CATEGORY_NAVIGATION, null);
+ } catch (CarNotConnectedException e) {
+ // This can happen only during shutdown, ignore.
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {}
+ };
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ Log.i(TAG, "onCreateView");
+ mDisplayManager = getActivity().getSystemService(DisplayManager.class);
+ mDisplayManager.registerDisplayListener(mDisplayListener, new Handler());
+
+ // Inflate the layout for this fragment
+ View root = inflater.inflate(R.layout.fragment_navigation, container, false);
+
+ mSurfaceView = root.findViewById(R.id.nav_surface);
+ mSurfaceView.getHolder().addCallback(new Callback() {
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ Log.i(TAG, "surfaceCreated, holder: " + holder);
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ Log.i(TAG, "surfaceChanged, holder: " + holder + ", size:" + width + "x" + height
+ + ", format:" + format);
+
+ //Create dummy unobscured area to report to navigation activity.
+ mUnobscuredBounds = new Rect(40, 0, width - 80, height - 40);
+
+ if (mVirtualDisplay == null) {
+ mVirtualDisplay = createVirtualDisplay(holder.getSurface(), width, height);
+ } else {
+ mVirtualDisplay.setSurface(holder.getSurface());
+ }
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ Log.i(TAG, "surfaceDestroyed, holder: " + holder + ", detaching surface from"
+ + " display, surface: " + holder.getSurface());
+ // detaching surface is similar to turning off the display
+ mVirtualDisplay.setSurface(null);
+ }
+ });
+
+ return root;
+ }
+
+ private VirtualDisplay createVirtualDisplay(Surface surface, int width, int height) {
+ Log.i(TAG, "createVirtualDisplay, surface: " + surface + ", width: " + width
+ + "x" + height);
+ return mDisplayManager.createVirtualDisplay("Cluster-App-VD", width, height, 160, surface,
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ Log.i(TAG, "onDestroy");
+ }
+
+ private SampleClusterServiceImpl getService() {
+ return ((MainClusterActivity) getActivity()).getService();
+ }
+
+ private int getVirtualDisplayId() {
+ return (mVirtualDisplay != null && mVirtualDisplay.getDisplay() != null)
+ ? mVirtualDisplay.getDisplay().getDisplayId() : Display.INVALID_DISPLAY;
+ }
+}
diff --git a/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/PhoneFragment.java b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/PhoneFragment.java
new file mode 100644
index 0000000..9930ec5
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/PhoneFragment.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster.sample;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+
+/**
+ * A simple {@link Fragment} subclass.
+ */
+public class PhoneFragment extends Fragment {
+
+
+ public PhoneFragment() {
+ // Required empty public constructor
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ return inflater.inflate(R.layout.fragment_phone, container, false);
+ }
+
+}
diff --git a/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/SampleClusterServiceImpl.java b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/SampleClusterServiceImpl.java
new file mode 100644
index 0000000..1457708
--- /dev/null
+++ b/tests/DirectRenderingClusterSample/src/android/car/cluster/sample/SampleClusterServiceImpl.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.car.cluster.sample;
+
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static java.lang.Integer.parseInt;
+
+import android.app.ActivityOptions;
+import android.car.CarNotConnectedException;
+import android.car.cluster.ClusterActivityState;
+import android.car.cluster.renderer.InstrumentClusterRenderingService;
+import android.car.cluster.renderer.NavigationRenderer;
+import android.car.navigation.CarNavigationInstrumentCluster;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.util.Log;
+import android.view.Display;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+/**
+ * Dummy implementation of {@link SampleClusterServiceImpl} to log all interaction.
+ */
+public class SampleClusterServiceImpl extends InstrumentClusterRenderingService {
+
+ private static final String TAG = SampleClusterServiceImpl.class.getSimpleName();
+
+ private Listener mListener;
+ private final Binder mLocalBinder = new LocalBinder();
+ static final String LOCAL_BINDING_ACTION = "local";
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ Log.i(TAG, "onBind, intent: " + intent);
+ return (LOCAL_BINDING_ACTION.equals(intent.getAction()))
+ ? mLocalBinder : super.onBind(intent);
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.i(TAG, "onCreate");
+
+ Display clusterDisplay = getInstrumentClusterDisplay(this);
+ if (clusterDisplay == null) {
+ Log.e(TAG, "Unable to find instrument cluster display");
+ return;
+ }
+
+ ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchDisplayId(clusterDisplay.getDisplayId());
+ Intent intent = new Intent(this, MainClusterActivity.class);
+ intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent, options.toBundle());
+ }
+
+ @Override
+ protected void onKeyEvent(KeyEvent keyEvent) {
+ Log.i(TAG, "onKeyEvent, keyEvent: " + keyEvent + ", listener: " + mListener);
+ if (mListener != null) {
+ mListener.onKeyEvent(keyEvent);
+ }
+ }
+
+ void registerListener(Listener listener) {
+ mListener = listener;
+ }
+
+ void unregisterListener() {
+ mListener = null;
+ }
+
+ @Override
+ protected NavigationRenderer getNavigationRenderer() {
+ NavigationRenderer navigationRenderer = new NavigationRenderer() {
+ @Override
+ public CarNavigationInstrumentCluster getNavigationProperties() {
+ Log.i(TAG, "getNavigationProperties");
+ CarNavigationInstrumentCluster config =
+ CarNavigationInstrumentCluster.createCluster(1000);
+ Log.i(TAG, "getNavigationProperties, returns: " + config);
+ return config;
+ }
+
+ @Override
+ public void onStartNavigation() {
+ Log.i(TAG, "onStartNavigation");
+ }
+
+ @Override
+ public void onStopNavigation() {
+ Log.i(TAG, "onStopNavigation");
+ }
+
+ @Override
+ public void onNextTurnChanged(int event, CharSequence eventName, int turnAngle,
+ int turnNumber, Bitmap image, int turnSide) {
+ Log.i(TAG, "event: " + event + ", eventName: " + eventName +
+ ", turnAngle: " + turnAngle + ", turnNumber: " + turnNumber +
+ ", image: " + image + ", turnSide: " + turnSide);
+ mListener.onShowToast("Next turn: " + eventName);
+ }
+
+ @Override
+ public void onNextTurnDistanceChanged(int distanceMeters, int timeSeconds,
+ int displayDistanceMillis, int displayDistanceUnit) {
+ Log.i(TAG, "onNextTurnDistanceChanged, distanceMeters: " + distanceMeters
+ + ", timeSeconds: " + timeSeconds
+ + ", displayDistanceMillis: " + displayDistanceMillis
+ + ", displayDistanceUnit: " + displayDistanceUnit);
+ mListener.onShowToast("Next turn distance: " + distanceMeters + " meters.");
+ }
+ };
+
+ Log.i(TAG, "createNavigationRenderer, returns: " + navigationRenderer);
+ return navigationRenderer;
+ }
+
+ class LocalBinder extends Binder {
+ SampleClusterServiceImpl getService() {
+ // Return this instance of LocalService so clients can call public methods
+ return SampleClusterServiceImpl.this;
+ }
+ }
+
+ interface Listener {
+ void onKeyEvent(KeyEvent event);
+ void onShowToast(String text);
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ if (args != null && args.length > 0) {
+ execShellCommand(args);
+ }
+ }
+
+ private void doKeyEvent(int keyCode) {
+ Log.i(TAG, "doKeyEvent, keyCode: " + keyCode);
+ long downTime = SystemClock.uptimeMillis();
+ long eventTime = SystemClock.uptimeMillis();
+ KeyEvent event = obtainKeyEvent(keyCode, downTime, eventTime, KeyEvent.ACTION_DOWN);
+ onKeyEvent(event);
+
+ eventTime = SystemClock.uptimeMillis();
+ event = obtainKeyEvent(keyCode, downTime, eventTime, KeyEvent.ACTION_UP);
+ onKeyEvent(event);
+ }
+
+ private KeyEvent obtainKeyEvent(int keyCode, long downTime, long eventTime, int action) {
+ int scanCode = 0;
+ if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
+ scanCode = 108;
+ } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
+ scanCode = 106;
+ }
+ return KeyEvent.obtain(
+ downTime,
+ eventTime,
+ action,
+ keyCode,
+ 0 /* repeat */,
+ 0 /* meta state */,
+ 0 /* deviceId*/,
+ scanCode /* scancode */,
+ KeyEvent.FLAG_FROM_SYSTEM /* flags */,
+ InputDevice.SOURCE_KEYBOARD,
+ null /* characters */);
+ }
+
+ private void execShellCommand(String[] args) {
+ Log.i(TAG, "execShellCommand, args: " + Arrays.toString(args));
+
+ String command = args[0];
+
+ switch (command) {
+ case "injectKey": {
+ if (args.length > 1) {
+ doKeyEvent(parseInt(args[1]));
+ } else {
+ Log.i(TAG, "Not enough arguments");
+ }
+ break;
+ }
+ case "destroyOverlayDisplay": {
+ Settings.Global.putString(getContentResolver(),
+ Global.OVERLAY_DISPLAY_DEVICES, "");
+ break;
+ }
+
+ case "createOverlayDisplay": {
+ if (args.length > 1) {
+ Settings.Global.putString(getContentResolver(),
+ Global.OVERLAY_DISPLAY_DEVICES, args[1]);
+ } else {
+ Log.i(TAG, "Not enough arguments, expected 2");
+ }
+ break;
+ }
+
+ case "setUnobscuredArea": {
+ if (args.length > 5) {
+ Rect unobscuredArea = new Rect(parseInt(args[2]), parseInt(args[3]),
+ parseInt(args[4]), parseInt(args[5]));
+ try {
+ setClusterActivityState(args[1],
+ ClusterActivityState.create(true, unobscuredArea).toBundle());
+ } catch (CarNotConnectedException e) {
+ Log.i(TAG, "Failed to set activity state.", e);
+ }
+ } else {
+ Log.i(TAG, "wrong format, expected: category left top right bottom");
+ }
+ }
+ }
+ }
+
+ private static Display getInstrumentClusterDisplay(Context context) {
+ DisplayManager displayManager = context.getSystemService(DisplayManager.class);
+ Display[] displays = displayManager.getDisplays();
+
+ Log.d(TAG, "There are currently " + displays.length + " displays connected.");
+ for (Display display : displays) {
+ Log.d(TAG, " " + display);
+ }
+
+ if (displays.length > 1) {
+ // TODO: assuming that secondary display is instrument cluster. Put this into settings?
+ return displays[1];
+ }
+ return null;
+ }
+
+}
diff --git a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
index 4d29276..2a96898 100644
--- a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
+++ b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
@@ -18,8 +18,8 @@
package="com.google.android.car.kitchensink"
android:sharedUserId="android.uid.system">
<uses-sdk
- android:minSdkVersion="22"
- android:targetSdkVersion='23'/>
+ android:minSdkVersion="24"
+ android:targetSdkVersion='25'/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
@@ -31,6 +31,8 @@
<uses-permission android:name="android.car.permission.CAR_CAMERA" />
<uses-permission android:name="android.car.permission.CAR_NAVIGATION_MANAGER"/>
<uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME"/>
+ <uses-permission android:name="android.car.permission.VEHICLE_DYNAMICS_STATE"/>
+ <uses-permission android:name="android.car.permission.CAR_DISPLAY_IN_CLUSTER"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.MANAGE_USB" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
@@ -88,12 +90,17 @@
android:launchMode="singleTop">
</activity>
- <receiver android:name=".bluetooth.MapReceiver"
- android:permission="android.permission.READ_SMS">
+ <activity android:name=".cluster.FakeClusterNavigationActivity"
+ android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+ android:launchMode="singleInstance"
+ android:resizeableActivity="true"
+ android:allowEmbedded="true"
+ android:permission="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL">
<intent-filter>
- <action android:name="android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED"/>
- <action android:name="android.provider.action.VOICE_SEND_MESSAGE_TO_CONTACTS"/>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.car.cluster.NAVIGATION"/>
</intent-filter>
- </receiver>
+ </activity>
</application>
</manifest>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/bluetooth_headset.xml b/tests/EmbeddedKitchenSinkApp/res/layout/bluetooth_headset.xml
index 0547a8f..5dbefc1 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/bluetooth_headset.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/bluetooth_headset.xml
@@ -57,4 +57,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bluetooth_quiet_mode_enable"/>
+ <Button
+ android:id="@+id/bluetooth_hold_call"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/bluetooth_hold_call"/>
</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/fake_cluster_navigation_activity.xml b/tests/EmbeddedKitchenSinkApp/res/layout/fake_cluster_navigation_activity.xml
new file mode 100644
index 0000000..11f0a6b
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/fake_cluster_navigation_activity.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/app_bg">
+
+ <ImageView
+ android:id="@+id/unobscuredArea"
+ android:alpha="0.25"
+ android:background="@android:color/white"
+ android:layout_height="0dp"
+ android:layout_width="0dp"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/instrument_cluster.xml b/tests/EmbeddedKitchenSinkApp/res/layout/instrument_cluster.xml
index 71c7fca..cb785a9 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/instrument_cluster.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/instrument_cluster.xml
@@ -17,26 +17,25 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginTop="160dp"
+ android:layout_marginTop="40dp"
android:layout_marginStart="40dp">
<LinearLayout
android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button android:layout_width="wrap_content" android:layout_height="wrap_content"
- android:text="@string/cluster_start" android:id="@+id/cluster_start_button"
- android:layout_column="0" android:textSize="32sp"/>
+ android:text="@string/cluster_start" android:id="@+id/cluster_start_button"/>
<Button android:layout_width="wrap_content" android:layout_height="wrap_content"
- android:text="@string/cluster_turn_left" android:id="@+id/cluster_turn_left_button"
- android:layout_column="0" android:textSize="32sp"/>
+ android:text="@string/cluster_turn_left" android:id="@+id/cluster_turn_left_button"/>
<Button android:layout_width="wrap_content" android:layout_height="wrap_content"
- android:text="@string/cluster_stop" android:id="@+id/cluster_stop_button"
- android:layout_column="0" android:textSize="32sp"/>
+ android:text="@string/cluster_stop" android:id="@+id/cluster_stop_button"/>
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="@string/cluster_start_activity" android:id="@+id/cluster_start_activity"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
\ No newline at end of file
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/kitchen_content.xml b/tests/EmbeddedKitchenSinkApp/res/layout/kitchen_content.xml
index 7554508..49497a2 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/kitchen_content.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/kitchen_content.xml
@@ -1,4 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
<!-- We use this container to place kitchen app fragments. It insets the fragment contents -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/kitchen_content"
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/radio.xml b/tests/EmbeddedKitchenSinkApp/res/layout/radio.xml
index e5b1ed2..590e7f1 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/radio.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/radio.xml
@@ -38,6 +38,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/radio_close" />
+ <ToggleButton
+ android:id="@+id/togglebutton_mute_radio"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textOn="@string/radio_muted"
+ android:textOff="@string/radio_unmuted" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
@@ -76,6 +82,33 @@
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_weight="1" >
+ <EditText
+ android:id="@+id/edittext_station_frequency"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:hint="@string/radio_enter_station_hint"
+ android:inputType="number" />
+ <Button
+ android:id="@+id/button_radio_tune_to_station"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/radio_tune_to_station" />
+ <Button
+ android:id="@+id/button_radio_step_up"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/radio_step_up" />
+ <Button
+ android:id="@+id/button_radio_step_down"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/radio_step_down" />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_weight="1" >
<ToggleButton
android:id="@+id/button_band_selection"
android:layout_width="wrap_content"
diff --git a/tests/EmbeddedKitchenSinkApp/res/raw/john_harrison_with_the_wichita_state_university_chamber_players_05_summer_mvt_2_adagio.mp3 b/tests/EmbeddedKitchenSinkApp/res/raw/john_harrison_with_the_wichita_state_university_chamber_players_05_summer_mvt_2_adagio.mp3
deleted file mode 100644
index 25e6be6..0000000
--- a/tests/EmbeddedKitchenSinkApp/res/raw/john_harrison_with_the_wichita_state_university_chamber_players_05_summer_mvt_2_adagio.mp3
+++ /dev/null
Binary files differ
diff --git a/tests/EmbeddedKitchenSinkApp/res/raw/well_worth_the_wait.mp3 b/tests/EmbeddedKitchenSinkApp/res/raw/well_worth_the_wait.mp3
new file mode 100644
index 0000000..a1db0bc
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/raw/well_worth_the_wait.mp3
Binary files differ
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
index e9214d9..fbba2a0 100644
--- a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
@@ -113,10 +113,11 @@
<string name="open_kb_button">Hide/Show Input</string>
<!-- instrument cluster -->
- <string name="cluster_start">Start Nav</string>
- <string name="cluster_turn_left">Turn left</string>
- <string name="cluster_stop">Stop Nav</string>
+ <string name="cluster_start">Start metadata</string>
+ <string name="cluster_turn_left">Send turn-by-turn</string>
+ <string name="cluster_stop">Stop metadata</string>
<string name="cluster_nav_app_context_loss">Navigation app context lost!</string>
+ <string name="cluster_start_activity">Start Nav Activity</string>
<!-- input test -->
<string name="volume_up">Volume +</string>
@@ -140,6 +141,8 @@
<!-- radio test -->
<string name="radio_open">Open</string>
<string name="radio_close">Close</string>
+ <string name="radio_muted">Muted</string>
+ <string name="radio_unmuted">Unmuted</string>
<string name="radio_get_radio_focus">Get Radio focus</string>
<string name="radio_release_radio_focus">Release Radio focus</string>
<string name="radio_get_focus">Get Audio focus</string>
@@ -147,6 +150,10 @@
<string name="radio_next">Next</string>
<string name="radio_prev">Previous</string>
<string name="radio_scan_cancel">Cancel scan</string>
+ <string name="radio_tune_to_station">Tune to Station</string>
+ <string name="radio_step_up">Step Up</string>
+ <string name="radio_step_down">Step Down</string>
+ <string name="radio_enter_station_hint">Enter Station Frequency (kHz)</string>
<string name="radio_get_program_info">getProgramInformation</string>
<string name="radio_am">AM</string>
<string name="radio_fm">FM</string>
@@ -156,24 +163,28 @@
<string name="radio_artist_info">Artist info: %1$s</string>
<string name="radio_na">N/A</string>
- <!-- sensorts test -->
+ <!-- sensors test -->
<string name="sensor_na">N/A</string>
<string name="sensor_environment">Environment[%1$s]: temperature=%2$s, pressure=%3$s</string>
<string name="sensor_night">Night[%1$s]: isNight=%2$s</string>
<string name="sensor_gear">Gear[%1$s]: gear=%2$s</string>
- <string name="sensor_parking_break">Parking break[%1$s]: isEngaged=%2$s</string>
- <string name="sensor_fuel_level">Fuel level[%1$s]: lebel=%2$s, range=%3$s, lowFuelWarning=%4$s</string>
+ <string name="sensor_parking_brake">Parking brake[%1$s]: isEngaged=%2$s</string>
+ <string name="sensor_fuel_level">Fuel level[%1$s]: level=%2$s, range=%3$s, lowFuelWarning=%4$s</string>
<string name="sensor_odometer">Odometer[%1$s]: kms=%2$s</string>
- <string name="sensor_rpm">Rpm[%1$s]: rpm=%2$s</string>
+ <string name="sensor_rpm">RPM[%1$s]: rpm=%2$s</string>
<string name="sensor_speed">Speed[%1$s]: speed=%2$s</string>
<string name="sensor_driving_status">Driving status[%1$s]: status=%2$s [bin=%3$s]</string>
<string name="sensor_compass">Compass[%1$s]: bear=%2$s, pitch=%3$s, roll=%4$s</string>
<string name="sensor_accelerometer">Accelerometer[%1$s]: x=%2$s, y=%3$s, z=%4$s</string>
- <string name="sensor_gyroscope">Gyroscpoe[%1$s]: x=%2$s, y=%3$s, z=%4$s</string>
+ <string name="sensor_gyroscope">Gyroscope[%1$s]: x=%2$s, y=%3$s, z=%4$s</string>
<string name="sensor_location">Location[%1$s]: lat=%2$s, lon=%3$s, accuracy=%4$s, altitude=%5$s, speed=%6$s, bearing=%7$s</string>
<string name="sensor_gps">GPS Satellites[%1$s]: inView: %2$s, inUse: %3$s. %4$s</string>
<string name="sensor_single_gps_satellite">(%1$s): usedInFix: %2$s, prn: %3$s, snr: %4$s, azimuth: %5$s, elevation: %6$s</string>
+ <string name="sensor_wheel_ticks">Wheel Distance[%1$s]: reset=%2$s, FL=%3$s, FR=%4$s, RL=%5$s, RR=%6$s</string>
+ <string name="sensor_wheel_ticks_cfg">Wheel Distance Config: Wheels=%1$s, FL=%2$s, FR=%3$s, RL=%4$s, RR=%5$s</string>
+ <string name="sensor_abs_is_active">ABS[%1$s]: isActive=%2$s</string>
+ <string name="sensor_traction_control_is_active">Traction Control[%1$s]: isActive=%2$s</string>
<string name="volume_test">Volume Test</string>
<string name="volume_up_logical">Vol +</string>
@@ -201,6 +212,7 @@
<string name="bluetooth_sco_disconnect">SCO disconnect</string>
<string name="bluetooth_pick_device">Pick Device</string>
<string name="bluetooth_quiet_mode_enable">Quiet Mode</string>
+ <string name="bluetooth_hold_call">Hold call</string>
<!--Car Service Settings-->
<string name="garage_title">Garage Mode</string>
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarEmulator.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarEmulator.java
index db1816b..7dddd1f 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarEmulator.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarEmulator.java
@@ -256,9 +256,9 @@
public void onPropertySet(VehiclePropValue value) {
ArrayList<Integer> v = value.value.int32Values;
- int stream = v.get(VehicleAudioVolumeIndex.INDEX_STREAM);
- int volume = v.get(VehicleAudioVolumeIndex.INDEX_VOLUME);
- int state = v.get(VehicleAudioVolumeIndex.INDEX_STATE);
+ int stream = v.get(VehicleAudioVolumeIndex.STREAM);
+ int volume = v.get(VehicleAudioVolumeIndex.VOLUME);
+ int state = v.get(VehicleAudioVolumeIndex.STATE);
VehiclePropValue injectValue =
VehiclePropValueBuilder.newBuilder(VehicleProperty.AUDIO_VOLUME)
@@ -272,7 +272,7 @@
@Override
public VehiclePropValue onPropertyGet(VehiclePropValue value) {
- int stream = value.value.int32Values.get(VehicleAudioVolumeIndex.INDEX_STREAM);
+ int stream = value.value.int32Values.get(VehicleAudioVolumeIndex.STREAM);
int volume = mCurrent.get(stream);
return VehiclePropValueBuilder.newBuilder(VehicleProperty.AUDIO_VOLUME)
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioPlayer.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioPlayer.java
index 618367d..74345aa 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioPlayer.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioPlayer.java
@@ -61,7 +61,8 @@
}
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
if (isPlaying()) {
- mPlayer.setVolume(0.5f, 0.5f);
+ // Duck to 20% volume (which matches system ducking as of this date)
+ mPlayer.setVolume(0.2f, 0.2f);
}
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT && mRepeat) {
if (isPlaying()) {
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
index dec0dc6..ce90651 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
@@ -175,7 +175,7 @@
//ignore for now
}
- mMusicPlayer = new AudioPlayer(mContext, R.raw.john_harrison_with_the_wichita_state_university_chamber_players_05_summer_mvt_2_adagio,
+ mMusicPlayer = new AudioPlayer(mContext, R.raw.well_worth_the_wait,
mMusicAudioAttrib);
mMusicPlayerShort = new AudioPlayer(mContext, R.raw.ring_classic_01,
mMusicAudioAttrib);
@@ -219,7 +219,9 @@
mMediaPlay.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- mMusicPlayer.start(false, true, AudioManager.AUDIOFOCUS_GAIN);
+ boolean requestFocus = true;
+ boolean repeat = true;
+ mMusicPlayer.start(requestFocus, repeat, AudioManager.AUDIOFOCUS_GAIN);
}
});
mMediaPlayOnce = (Button) view.findViewById(R.id.button_media_play_once);
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/BluetoothHeadsetFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/BluetoothHeadsetFragment.java
index 30804fe..cb668c4 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/BluetoothHeadsetFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/BluetoothHeadsetFragment.java
@@ -57,15 +57,17 @@
Button mScoConnect;
Button mScoDisconnect;
Button mEnableQuietMode;
+ Button mHoldCall;
BluetoothHeadsetClient mHfpClientProfile;
// Intent for picking a Bluetooth device
- public static final String DEVICE_PICKER_ACTION = "android.bluetooth.devicepicker.action.LAUNCH";
+ public static final String DEVICE_PICKER_ACTION =
+ "android.bluetooth.devicepicker.action.LAUNCH";
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
+ @Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.bluetooth_headset, container, false);
mPickedDeviceText = (TextView) v.findViewById(R.id.bluetooth_device);
@@ -73,8 +75,9 @@
mConnect = (Button) v.findViewById(R.id.bluetooth_headset_connect);
mDisconnect = (Button) v.findViewById(R.id.bluetooth_headset_disconnect);
mScoConnect = (Button) v.findViewById(R.id.bluetooth_sco_connect);
- mScoDisconnect= (Button) v.findViewById(R.id.bluetooth_sco_disconnect);
+ mScoDisconnect = (Button) v.findViewById(R.id.bluetooth_sco_disconnect);
mEnableQuietMode = (Button) v.findViewById(R.id.bluetooth_quiet_mode_enable);
+ mHoldCall = (Button) v.findViewById(R.id.bluetooth_hold_call);
// Pick a bluetooth device
mDevicePicker.setOnClickListener(new View.OnClickListener() {
@@ -123,6 +126,14 @@
mBluetoothAdapter.enableNoAutoConnect();
}
});
+
+ // Place the current call on hold
+ mHoldCall.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ holdCall();
+ }
+ });
return v;
}
@@ -192,6 +203,20 @@
mHfpClientProfile.disconnectAudio(mPickedDevice);
}
+ void holdCall() {
+ if (mPickedDevice == null) {
+ Log.w(TAG, "Device null when trying to put the call on hold!");
+ return;
+ }
+
+ if (mHfpClientProfile == null) {
+ Log.w(TAG, "HFP Profile proxy not available, cannot put the call on hold " +
+ mPickedDevice);
+ return;
+ }
+ mHfpClientProfile.holdCall(mPickedDevice);
+ }
+
private final BroadcastReceiver mPickerReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapReceiver.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapReceiver.java
deleted file mode 100644
index e0d1ca0..0000000
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/bluetooth/MapReceiver.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.car.kitchensink.bluetooth;
-
-import android.bluetooth.BluetoothMapClient;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Log;
-import android.widget.Toast;
-
-public class MapReceiver extends BroadcastReceiver {
- private static final String TAG = "CAR.BLUETOOTH.KS";
- @Override
- public void onReceive(Context context, Intent intent) {
- Log.d(TAG, "MAP onReceive");
- String action = intent.getAction();
- if (action.equals(BluetoothMapClient.ACTION_MESSAGE_RECEIVED)) {
- Toast.makeText(context, intent.getStringExtra(android.content.Intent.EXTRA_TEXT),
- Toast.LENGTH_LONG).show();
- }
- }
-}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/FakeClusterNavigationActivity.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/FakeClusterNavigationActivity.java
new file mode 100644
index 0000000..964d812
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/FakeClusterNavigationActivity.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.car.kitchensink.cluster;
+
+import android.app.Activity;
+import android.car.cluster.CarInstrumentClusterManager;
+import android.car.cluster.ClusterActivityState;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.support.car.Car;
+import android.support.car.CarConnectionCallback;
+import android.support.car.CarNotConnectedException;
+import android.util.Log;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+
+import com.google.android.car.kitchensink.R;
+
+/**
+ * Fake navigation activity for instrument cluster.
+ */
+public class FakeClusterNavigationActivity
+ extends Activity
+ implements CarInstrumentClusterManager.Callback {
+
+ private final static String TAG = FakeClusterNavigationActivity.class.getSimpleName();
+
+ private Car mCarApi;
+ private CarInstrumentClusterManager mClusterManager;
+ private ImageView mUnobscuredArea;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.i(TAG, "onCreate");
+ setContentView(R.layout.fake_cluster_navigation_activity);
+ mUnobscuredArea = findViewById(R.id.unobscuredArea);
+
+ mCarApi = Car.createCar(this /* context */, new CarConnectionCallback() {
+
+ @Override
+ public void onConnected(Car car) {
+ onCarConnected(car);
+ }
+
+ @Override
+ public void onDisconnected(Car car) {
+ onCarDisconnected(car);
+ }
+ });
+ Log.i(TAG, "Connecting to car api...");
+ mCarApi.connect();
+ }
+
+
+ @Override
+ public void onClusterActivityStateChanged(String category, Bundle clusterActivityState) {
+ ClusterActivityState state = ClusterActivityState.fromBundle(clusterActivityState);
+ Log.i(TAG, "onClusterActivityStateChanged, category: " + category + ", state: " + state);
+
+ Rect unobscured = state.getUnobscuredBounds();
+ RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
+ unobscured.width(), unobscured.height());
+ lp.setMargins(unobscured.left, unobscured.top, 0, 0);
+ mUnobscuredArea.setLayoutParams(lp);
+ }
+
+ private void onCarConnected(Car car) {
+ Log.i(TAG, "onCarConnected, car: " + car);
+ try {
+ mClusterManager = (CarInstrumentClusterManager) car.getCarManager(
+ android.car.Car.CAR_INSTRUMENT_CLUSTER_SERVICE);
+ } catch (CarNotConnectedException e) {
+ throw new IllegalStateException(e);
+ }
+
+ try {
+ Log.i(TAG, "registering callback...");
+ mClusterManager.registerCallback(CarInstrumentClusterManager.CATEGORY_NAVIGATION, this);
+ Log.i(TAG, "callback registered");
+ } catch (android.car.CarNotConnectedException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ private void onCarDisconnected(Car car) {
+
+ }
+}
\ No newline at end of file
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java
index 28e0a5d..cfae45f 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java
@@ -16,18 +16,22 @@
package com.google.android.car.kitchensink.cluster;
import android.app.AlertDialog;
+import android.car.cluster.CarInstrumentClusterManager;
+import android.content.Intent;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.car.Car;
import android.support.car.CarAppFocusManager;
-import android.support.car.CarNotConnectedException;
import android.support.car.CarConnectionCallback;
+import android.support.car.CarNotConnectedException;
import android.support.car.navigation.CarNavigationStatusManager;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.Toast;
import com.google.android.car.kitchensink.R;
@@ -37,30 +41,30 @@
public class InstrumentClusterFragment extends Fragment {
private static final String TAG = InstrumentClusterFragment.class.getSimpleName();
+ private static final int DISPLAY_IN_CLUSTER_PERMISSION_REQUEST = 1;
+
private CarNavigationStatusManager mCarNavigationStatusManager;
private CarAppFocusManager mCarAppFocusManager;
private Car mCarApi;
- private final CarConnectionCallback mCarConnectionCallback =
- new CarConnectionCallback() {
- @Override
- public void onConnected(Car car) {
- Log.d(TAG, "Connected to Car Service");
- try {
- mCarNavigationStatusManager = (CarNavigationStatusManager) mCarApi.getCarManager(
- android.car.Car.CAR_NAVIGATION_SERVICE);
- mCarAppFocusManager =
- (CarAppFocusManager) mCarApi.getCarManager(Car.APP_FOCUS_SERVICE);
- } catch (CarNotConnectedException e) {
- Log.e(TAG, "Car is not connected!", e);
- }
+ private final CarConnectionCallback mCarConnectionCallback = new CarConnectionCallback() {
+ @Override
+ public void onConnected(Car car) {
+ Log.d(TAG, "Connected to Car Service");
+ try {
+ mCarNavigationStatusManager =
+ mCarApi.getCarManager(CarNavigationStatusManager.class);
+ mCarAppFocusManager = mCarApi.getCarManager(CarAppFocusManager.class);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
}
+ }
- @Override
- public void onDisconnected(Car car) {
- Log.d(TAG, "Disconnect from Car Service");
- }
- };
+ @Override
+ public void onDisconnected(Car car) {
+ Log.d(TAG, "Disconnect from Car Service");
+ }
+ };
private void initCarApi() {
if (mCarApi != null && mCarApi.isConnected()) {
@@ -80,6 +84,7 @@
view.findViewById(R.id.cluster_start_button).setOnClickListener(v -> initCluster());
view.findViewById(R.id.cluster_turn_left_button).setOnClickListener(v -> turnLeft());
+ view.findViewById(R.id.cluster_start_activity).setOnClickListener(v -> startNavActivity());
return view;
}
@@ -91,6 +96,31 @@
super.onCreate(savedInstanceState);
}
+ private void startNavActivity() {
+ CarInstrumentClusterManager clusterManager;
+ try {
+ clusterManager = (CarInstrumentClusterManager) mCarApi.getCarManager(
+ android.car.Car.CAR_INSTRUMENT_CLUSTER_SERVICE);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Failed to get CarInstrumentClusterManager", e);
+ Toast.makeText(getContext(), "Failed to get CarInstrumentClusterManager",
+ Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ // Implicit intent
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(CarInstrumentClusterManager.CATEGORY_NAVIGATION);
+ try {
+ clusterManager.startActivity(intent);
+ } catch (android.car.CarNotConnectedException e) {
+ Log.e(TAG, "Failed to startActivity in cluster", e);
+ Toast.makeText(getContext(), "Failed to start activity in cluster",
+ Toast.LENGTH_LONG).show();
+ return;
+ }
+ }
+
private void turnLeft() {
try {
mCarNavigationStatusManager
@@ -100,18 +130,20 @@
CarNavigationStatusManager.DISTANCE_METERS);
} catch (CarNotConnectedException e) {
e.printStackTrace();
- initCarApi(); // This might happen due to inst cluster renderer crash.
}
}
private void initCluster() {
try {
- mCarAppFocusManager.addFocusListener(new CarAppFocusManager.OnAppFocusChangedListener() {
- @Override
- public void onAppFocusChanged(CarAppFocusManager manager, int appType, boolean active) {
- Log.d(TAG, "onAppFocusChanged, appType: " + appType + " active: " + active);
- }
- }, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ mCarAppFocusManager
+ .addFocusListener(new CarAppFocusManager.OnAppFocusChangedListener() {
+ @Override
+ public void onAppFocusChanged(CarAppFocusManager manager, int appType,
+ boolean active) {
+ Log.d(TAG, "onAppFocusChanged, appType: " + appType + " active: "
+ + active);
+ }
+ }, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
} catch (CarNotConnectedException e) {
Log.e(TAG, "Failed to register focus listener", e);
}
@@ -126,6 +158,7 @@
.setMessage(R.string.cluster_nav_app_context_loss)
.show();
}
+
@Override
public void onAppFocusOwnershipGranted(CarAppFocusManager manager, int focus) {
Log.w(TAG, "onAppFocusOwnershipGranted, focus: " + focus);
@@ -155,7 +188,33 @@
.sendNavigationStatus(CarNavigationStatusManager.STATUS_ACTIVE);
} catch (CarNotConnectedException e) {
Log.e(TAG, "Failed to set navigation status, reconnecting to the car", e);
- initCarApi(); // This might happen due to inst cluster renderer crash.
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ Log.i(TAG, "onResume!");
+ if (getActivity().checkSelfPermission(android.car.Car.PERMISSION_CAR_DISPLAY_IN_CLUSTER)
+ != PackageManager.PERMISSION_GRANTED) {
+ Log.i(TAG, "Requesting: " + android.car.Car.PERMISSION_CAR_DISPLAY_IN_CLUSTER);
+
+ requestPermissions(new String[] {android.car.Car.PERMISSION_CAR_DISPLAY_IN_CLUSTER},
+ DISPLAY_IN_CLUSTER_PERMISSION_REQUEST);
+ } else {
+ Log.i(TAG, "All required permissions granted");
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ int[] grantResults) {
+ if (DISPLAY_IN_CLUSTER_PERMISSION_REQUEST == requestCode) {
+ for (int i = 0; i < permissions.length; i++) {
+ boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED;
+ Log.i(TAG, "onRequestPermissionsResult, requestCode: " + requestCode
+ + ", permission: " + permissions[i] + ", granted: " + granted);
+ }
}
}
}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/diagnostic/DiagnosticTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/diagnostic/DiagnosticTestFragment.java
index 71deee8..b024bfd 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/diagnostic/DiagnosticTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/diagnostic/DiagnosticTestFragment.java
@@ -18,12 +18,11 @@
import android.annotation.Nullable;
import android.car.Car;
-import android.car.hardware.CarDiagnosticEvent;
-import android.car.hardware.CarDiagnosticManager;
-import android.car.hardware.CarDiagnosticManager.OnDiagnosticEventListener;
+import android.car.diagnostic.CarDiagnosticEvent;
+import android.car.diagnostic.CarDiagnosticManager;
+import android.car.diagnostic.CarDiagnosticManager.OnDiagnosticEventListener;
import android.graphics.Color;
import android.os.Bundle;
-import android.os.Handler;
import android.support.car.hardware.CarSensorManager;
import android.support.v4.app.Fragment;
import android.util.Log;
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/hvac/HvacTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/hvac/HvacTestFragment.java
index 3f5ef86..756ac0b 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/hvac/HvacTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/hvac/HvacTestFragment.java
@@ -167,12 +167,9 @@
}
};
- public HvacTestFragment() {
- setHvacManager( ((KitchenSinkActivity)getActivity()).getHvacManager() );
- }
-
@Override
public void onCreate(Bundle savedInstanceState) {
+ mCarHvacManager = ((KitchenSinkActivity)getActivity()).getHvacManager();
super.onCreate(savedInstanceState);
try {
mCarHvacManager.registerCallback(mHvacCallback);
@@ -262,11 +259,6 @@
return v;
}
- public void setHvacManager(CarHvacManager hvacManager) {
- Log.d(TAG, "setHvacManager()");
- mCarHvacManager = hvacManager;
- }
-
private void configureOutsideTemp(View v, CarPropertyConfig prop) {
// Do nothing
}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/radio/RadioTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/radio/RadioTestFragment.java
index baba61b..0214791 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/radio/RadioTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/radio/RadioTestFragment.java
@@ -41,6 +41,7 @@
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.EditText;
import android.widget.TextView;
import android.widget.ToggleButton;
@@ -92,7 +93,7 @@
}
mArtist = metadata.getString(RadioMetadata.METADATA_KEY_ARTIST);
mSong = metadata.getString(RadioMetadata.METADATA_KEY_TITLE);
- mStation = metadata.getString(RadioMetadata.METADATA_KEY_RDS_PI);
+ mStation = metadata.getString(RadioMetadata.METADATA_KEY_RDS_PS);
updateMessages();
}
@@ -117,6 +118,11 @@
private Button mRadioPrev;
private Button mRadioScanCancel;
private Button mRadioGetProgramInfo;
+ private Button mRadioTuneToStation;
+ private Button mRadioStepUp;
+ private Button mRadioStepDown;
+ private EditText mStationFrequency;
+ private ToggleButton mToggleMuteRadio;
private ToggleButton mRadioBand;
private TextView mStationInfo;
private TextView mChannelInfo;
@@ -166,6 +172,14 @@
mRadioPrev = (Button) view.findViewById(R.id.button_radio_prev);
mRadioScanCancel = (Button) view.findViewById(R.id.button_radio_scan_cancel);
mRadioGetProgramInfo = (Button) view.findViewById(R.id.button_radio_get_program_info);
+ mRadioTuneToStation = (Button) view.findViewById(R.id.button_radio_tune_to_station);
+ mRadioStepUp = (Button) view.findViewById(R.id.button_radio_step_up);
+ mRadioStepDown = (Button) view.findViewById(R.id.button_radio_step_down);
+
+ mStationFrequency = (EditText) view.findViewById(R.id.edittext_station_frequency);
+
+ mToggleMuteRadio = (ToggleButton) view.findViewById(R.id.togglebutton_mute_radio);
+ mToggleMuteRadio.setChecked(true);
mRadioBand = (ToggleButton) view.findViewById(R.id.button_band_selection);
mStationInfo = (TextView) view.findViewById(R.id.radio_station_info);
@@ -211,8 +225,7 @@
}
});
mCar.connect();
- mAudioManager = (AudioManager) getContext().getSystemService(
- Context.AUDIO_SERVICE);
+ mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
initializeRadio();
}
@@ -240,11 +253,11 @@
Log.d(TAG, "loading band: " + band.toString());
}
- if (mFmDescriptor == null && band.getType() == RadioManager.BAND_FM) {
+ if (mFmDescriptor == null && band.isFmBand()) {
mFmDescriptor = (RadioManager.FmBandDescriptor) band;
}
- if (mAmDescriptor == null && band.getType() == RadioManager.BAND_AM) {
+ if (mAmDescriptor == null && band.isAmBand()) {
mAmDescriptor = (RadioManager.AmBandDescriptor) band;
}
}
@@ -273,6 +286,17 @@
@Override
public void onClick(View v) {
handleRadioEnd();
+ mToggleMuteRadio.setChecked(true);
+ updateStates();
+ }
+ });
+ mToggleMuteRadio.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (DBG) {
+ Log.i(TAG, "Toggle mute radio");
+ }
+ mRadioTuner.setMute(!mRadioTuner.getMute());
updateStates();
}
});
@@ -362,6 +386,49 @@
updateStates();
}
});
+ mRadioTuneToStation.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (DBG) {
+ Log.i(TAG, "Tuning to station");
+ }
+ String station = mStationFrequency.getText().toString().trim();
+ if (mRadioTuner != null && !(station.equals(""))) {
+ mRadioTuner.tune(Integer.parseInt(station), 0);
+ }
+ resetMessages();
+ updateMessages();
+ updateStates();
+ }
+ });
+ mRadioStepUp.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (DBG) {
+ Log.i(TAG, "Step up");
+ }
+ if (mRadioTuner != null) {
+ mRadioTuner.step(RadioTuner.DIRECTION_UP, false);
+ }
+ resetMessages();
+ updateMessages();
+ updateStates();
+ }
+ });
+ mRadioStepDown.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (DBG) {
+ Log.i(TAG, "Step down");
+ }
+ if (mRadioTuner != null) {
+ mRadioTuner.step(RadioTuner.DIRECTION_DOWN, false);
+ }
+ resetMessages();
+ updateMessages();
+ updateStates();
+ }
+ });
mRadioGetProgramInfo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -394,6 +461,7 @@
private void updateStates() {
mOpenRadio.setEnabled(mRadioTuner == null);
mCloseRadio.setEnabled(mRadioTuner != null);
+ mToggleMuteRadio.setEnabled(mRadioTuner != null);
mGetRadioFocus.setEnabled(!mHasRadioFocus);
mReleaseRadioFocus.setEnabled(mHasRadioFocus);
mGetFocus.setEnabled(!mHasSecondaryFocus);
@@ -402,6 +470,10 @@
mRadioPrev.setEnabled(mRadioTuner != null);
mRadioBand.setEnabled(mRadioTuner != null);
mRadioScanCancel.setEnabled(mRadioTuner != null);
+ mRadioTuneToStation.setEnabled(mRadioTuner != null);
+ mRadioStepUp.setEnabled(mRadioTuner != null);
+ mRadioStepDown.setEnabled(mRadioTuner != null);
+ mStationFrequency.setEnabled(mRadioTuner != null);
mRadioGetProgramInfo.setEnabled(mRadioTuner != null);
}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/sensor/SensorsTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/sensor/SensorsTestFragment.java
index 262e473..d502c75 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/sensor/SensorsTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/sensor/SensorsTestFragment.java
@@ -24,6 +24,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.support.car.CarNotConnectedException;
+import android.support.car.hardware.CarSensorConfig;
import android.support.car.hardware.CarSensorEvent;
import android.support.car.hardware.CarSensorManager;
import android.support.v4.app.Fragment;
@@ -58,7 +59,8 @@
Manifest.permission.ACCESS_COARSE_LOCATION,
Car.PERMISSION_MILEAGE,
Car.PERMISSION_FUEL,
- Car.PERMISSION_SPEED
+ Car.PERMISSION_SPEED,
+ Car.PERMISSION_VEHICLE_DYNAMICS_STATE
};
private final CarSensorManager.OnSensorChangedListener mOnSensorChangedListener =
@@ -219,7 +221,7 @@
getTimestamp(event), level, range, lowFuelWarning));
break;
case CarSensorManager.SENSOR_TYPE_PARKING_BRAKE:
- summary.add(getContext().getString(R.string.sensor_parking_break,
+ summary.add(getContext().getString(R.string.sensor_parking_brake,
getTimestamp(event),
event == null ? mNaString :
event.getParkingBrakeData().isEngaged));
@@ -271,6 +273,45 @@
case CarSensorManager.SENSOR_TYPE_GYROSCOPE:
summary.add(getGyroscopeString(event));
break;
+ case CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE:
+ if(event != null) {
+ CarSensorEvent.CarWheelTickDistanceData d =
+ event.getCarWheelTickDistanceData();
+ summary.add(getContext().getString(R.string.sensor_wheel_ticks,
+ getTimestamp(event), d.sensorResetCount, d.frontLeftWheelDistanceMm,
+ d.frontRightWheelDistanceMm, d.rearLeftWheelDistanceMm,
+ d.rearRightWheelDistanceMm));
+ } else {
+ summary.add(getContext().getString(R.string.sensor_wheel_ticks,
+ getTimestamp(event), mNaString, mNaString, mNaString, mNaString,
+ mNaString));
+ }
+ // Get the config data
+ try {
+ CarSensorConfig c = mSensorManager.getSensorConfig(
+ CarSensorManager.SENSOR_TYPE_WHEEL_TICK_DISTANCE);
+ summary.add(getContext().getString(R.string.sensor_wheel_ticks_cfg,
+ c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_SUPPORTED_WHEELS),
+ c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_LEFT_UM_PER_TICK),
+ c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_FRONT_RIGHT_UM_PER_TICK),
+ c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_LEFT_UM_PER_TICK),
+ c.getInt(CarSensorConfig.WHEEL_TICK_DISTANCE_REAR_RIGHT_UM_PER_TICK)));
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car not connected or not supported", e);
+ }
+ break;
+ case CarSensorManager.SENSOR_TYPE_ABS_ACTIVE:
+ summary.add(getContext().getString(R.string.sensor_abs_is_active,
+ getTimestamp(event), event == null ? mNaString :
+ event.getCarAbsActiveData().absIsActive));
+ break;
+
+ case CarSensorManager.SENSOR_TYPE_TRACTION_CONTROL_ACTIVE:
+ summary.add(
+ getContext().getString(R.string.sensor_traction_control_is_active,
+ getTimestamp(event), event == null ? mNaString :
+ event.getCarTractionControlActiveData().tractionControlIsActive));
+ break;
default:
// Should never happen.
Log.w(TAG, "Unrecognized event type: " + i);
diff --git a/tests/VmsPublisherClientSample/src/com/google/android/car/vms/publisher/VmsPublisherClientSampleService.java b/tests/VmsPublisherClientSample/src/com/google/android/car/vms/publisher/VmsPublisherClientSampleService.java
index 08d37cd..e235f1e 100644
--- a/tests/VmsPublisherClientSample/src/com/google/android/car/vms/publisher/VmsPublisherClientSampleService.java
+++ b/tests/VmsPublisherClientSample/src/com/google/android/car/vms/publisher/VmsPublisherClientSampleService.java
@@ -30,7 +30,8 @@
*/
public class VmsPublisherClientSampleService extends VmsPublisherClientService {
public static final int PUBLISH_EVENT = 0;
- public static final VmsLayer TEST_LAYER = new VmsLayer(0, 0);
+ public static final VmsLayer TEST_LAYER = new VmsLayer(0, 0, 0);
+ public static final int PUBLISHER_ID = 1;
private byte mCounter = 0;
private AtomicBoolean mInitialized = new AtomicBoolean(false);
@@ -73,7 +74,7 @@
}
private void periodicPublish() {
- publish(TEST_LAYER, new byte[]{mCounter});
+ publish(TEST_LAYER, PUBLISHER_ID, new byte[]{mCounter});
++mCounter;
mHandler.sendEmptyMessageDelayed(PUBLISH_EVENT, 1000);
}
diff --git a/tests/VmsSubscriberClientSample/src/com/google/android/car/vms/subscriber/VmsSubscriberClientSampleActivity.java b/tests/VmsSubscriberClientSample/src/com/google/android/car/vms/subscriber/VmsSubscriberClientSampleActivity.java
index fe32ab9..3ac0707 100644
--- a/tests/VmsSubscriberClientSample/src/com/google/android/car/vms/subscriber/VmsSubscriberClientSampleActivity.java
+++ b/tests/VmsSubscriberClientSample/src/com/google/android/car/vms/subscriber/VmsSubscriberClientSampleActivity.java
@@ -25,6 +25,7 @@
import android.support.car.CarConnectionCallback;
import android.util.Log;
import android.widget.TextView;
+
import java.util.List;
/**
@@ -36,7 +37,7 @@
private static final String TAG = "VmsSampleActivity";
// The layer id and version should match the ones defined in
// com.google.android.car.vms.publisher.VmsPublisherClientSampleService
- public static final VmsLayer TEST_LAYER = new VmsLayer(0, 0);
+ public static final VmsLayer TEST_LAYER = new VmsLayer(0, 0, 0);
private Car mCarApi;
private TextView mTextView;
@@ -89,7 +90,7 @@
private void configureSubscriptions(VmsSubscriberManager vmsSubscriberManager) {
try {
- vmsSubscriberManager.setListener(mListener);
+ vmsSubscriberManager.registerClientCallback(mClientCallback);
vmsSubscriberManager.subscribe(TEST_LAYER);
} catch (android.car.CarNotConnectedException e) {
Log.e(TAG, "Car is not connected!", e);
@@ -97,21 +98,16 @@
}
};
- private final VmsSubscriberManager.VmsSubscriberClientListener mListener =
- new VmsSubscriberManager.VmsSubscriberClientListener() {
+ private final VmsSubscriberManager.VmsSubscriberClientCallback mClientCallback =
+ new VmsSubscriberManager.VmsSubscriberClientCallback() {
@Override
public void onVmsMessageReceived(VmsLayer layer, byte[] payload) {
mTextView.setText(String.valueOf(payload[0]));
}
@Override
- public void onLayersAvailabilityChange(List<VmsLayer> availableLayers) {
+ public void onLayersAvailabilityChanged(List<VmsLayer> availableLayers) {
mTextView.setText(String.valueOf(availableLayers));
}
-
- @Override
- public void onCarDisconnected() {
-
- }
};
}
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarDiagnosticManagerTest.java b/tests/android_car_api_test/src/android/car/apitest/CarDiagnosticManagerTest.java
index 21fb5e0..8783341 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarDiagnosticManagerTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarDiagnosticManagerTest.java
@@ -17,8 +17,8 @@
package android.car.apitest;
import android.car.Car;
-import android.car.hardware.CarDiagnosticEvent;
-import android.car.hardware.CarDiagnosticManager;
+import android.car.diagnostic.CarDiagnosticEvent;
+import android.car.diagnostic.CarDiagnosticManager;
import android.content.ComponentName;
import android.content.ServiceConnection;
import android.os.IBinder;
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarInfoManagerTest.java b/tests/android_car_api_test/src/android/car/apitest/CarInfoManagerTest.java
index 9cf21e5..3a94b39 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarInfoManagerTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarInfoManagerTest.java
@@ -19,7 +19,9 @@
import android.car.Car;
import android.car.CarInfoManager;
import android.os.Bundle;
+import android.test.suitebuilder.annotation.SmallTest;
+@SmallTest
public class CarInfoManagerTest extends CarApiTestBase {
private CarInfoManager mInfoManager;
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);
}
}
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarProjectionManagerTest.java b/tests/android_car_api_test/src/android/car/apitest/CarProjectionManagerTest.java
index 973815b..889a60a 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarProjectionManagerTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarProjectionManagerTest.java
@@ -68,14 +68,14 @@
}
public void testSetUnsetListeners() throws Exception {
- mManager.regsiterProjectionListener(
+ mManager.registerProjectionListener(
mListener, CarProjectionManager.PROJECTION_VOICE_SEARCH);
- mManager.unregsiterProjectionListener();
+ mManager.unregisterProjectionListener();
}
public void testRegisterListenersHandleBadInput() throws Exception {
try {
- mManager.regsiterProjectionListener(null, CarProjectionManager.PROJECTION_VOICE_SEARCH);
+ mManager.registerProjectionListener(null, CarProjectionManager.PROJECTION_VOICE_SEARCH);
fail();
} catch (IllegalArgumentException e) {
// expected.
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarPropertyConfigTest.java b/tests/android_car_api_test/src/android/car/apitest/CarPropertyConfigTest.java
index 592a3f6..add6acc 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarPropertyConfigTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarPropertyConfigTest.java
@@ -18,10 +18,12 @@
import android.car.hardware.CarPropertyConfig;
import android.graphics.Point;
+import android.test.suitebuilder.annotation.MediumTest;
/**
* Unit tests for {@link CarPropertyConfig}
*/
+@MediumTest
public class CarPropertyConfigTest extends CarPropertyTestBase {
public void testCarPropertyConfigBuilder() {
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarPropertyValueTest.java b/tests/android_car_api_test/src/android/car/apitest/CarPropertyValueTest.java
index 0613f5f..a8f3f90 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarPropertyValueTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarPropertyValueTest.java
@@ -18,10 +18,12 @@
import android.car.hardware.CarPropertyValue;
import android.graphics.Point;
+import android.test.suitebuilder.annotation.MediumTest;
/**
* Unit tests for {@link CarPropertyValue}
*/
+@MediumTest
public class CarPropertyValueTest extends CarPropertyConfigTest {
public void testSimpleFloatValue() {
diff --git a/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarNavigationStatusManagerTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarNavigationStatusManagerTest.java
index 5b2aa5a..1972c48 100644
--- a/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarNavigationStatusManagerTest.java
+++ b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarNavigationStatusManagerTest.java
@@ -23,6 +23,7 @@
import android.support.car.CarAppFocusManager.OnAppFocusOwnershipCallback;
import android.support.car.navigation.CarNavigationInstrumentCluster;
import android.support.car.navigation.CarNavigationStatusManager;
+import android.test.suitebuilder.annotation.MediumTest;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -30,6 +31,7 @@
/**
* Unit tests for {@link android.support.car.navigation.CarNavigationStatusManager}
*/
+@MediumTest
public class CarNavigationStatusManagerTest extends CarApiTestBase {
private CarNavigationStatusManager mCarNavigationStatusManager;
diff --git a/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java
index eb1e927..c60496b 100644
--- a/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java
+++ b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarSensorManagerTest.java
@@ -22,6 +22,7 @@
import android.support.car.hardware.CarSensorEvent;
import android.support.car.hardware.CarSensorManager;
import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
import java.lang.reflect.Field;
import java.util.HashMap;
@@ -31,7 +32,7 @@
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
-
+@MediumTest
public class CarSensorManagerTest extends AndroidTestCase {
private static final long DEFAULT_WAIT_TIMEOUT_MS = 3000;
@@ -109,22 +110,24 @@
}
StringBuilder builder = new StringBuilder();
boolean failed = false;
- for (Field supprotCarSensorType : supportCarSensorTypes) {
+ for (Field supportCarSensorType : supportCarSensorTypes) {
Field androidCarSensorType = androidCarSensorTypeToField.get(
- supprotCarSensorType.getInt(null));
- assertNotNull("Sensor type:" + supprotCarSensorType.getName() +
+ supportCarSensorType.getInt(null));
+ assertNotNull("Sensor type:" + supportCarSensorType.getName() +
" not defined in android.car", androidCarSensorType);
- if (supprotCarSensorType.getName().equals(androidCarSensorType.getName())) {
+ if (supportCarSensorType.getName().equals(androidCarSensorType.getName())) {
// match ok
} else if (androidCarSensorType.getName().startsWith("SENSOR_TYPE_RESERVED")) {
// not used in android.car, ok
+ } else if (supportCarSensorType.getName().startsWith("SENSOR_TYPE_RESERVED")) {
+ // used in android.car but reserved in support.car
} else {
failed = true;
- builder.append("android.support sensor has name:" + supprotCarSensorType.getName() +
+ builder.append("android.support sensor has name:" + supportCarSensorType.getName() +
" while android.car sensor has name:" + androidCarSensorType.getName() +
"\n");
}
- androidCarSensorTypeToField.remove(supprotCarSensorType.getInt(null));
+ androidCarSensorTypeToField.remove(supportCarSensorType.getInt(null));
}
assertFalse(builder.toString(), failed);
assertTrue("android Car sensor has additional types defined:" + androidCarSensorTypeToField,
diff --git a/tests/carservice_test/Android.mk b/tests/carservice_test/Android.mk
index 105a73a..4be5354 100644
--- a/tests/carservice_test/Android.mk
+++ b/tests/carservice_test/Android.mk
@@ -40,7 +40,8 @@
LOCAL_STATIC_JAVA_LIBRARIES += car-service-lib-for-test \
vehicle-hal-support-lib \
car-systemtest \
- android-support-test
+ android-support-test \
+ android.hardware.automotive.vehicle-V2.0-java-static
LOCAL_JAVA_LIBRARIES := android.car android.test.runner
diff --git a/tests/carservice_test/src/com/android/car/test/AudioRoutingPolicyTest.java b/tests/carservice_test/src/com/android/car/test/AudioRoutingPolicyTest.java
index 6363589..7f4aeac 100644
--- a/tests/carservice_test/src/com/android/car/test/AudioRoutingPolicyTest.java
+++ b/tests/carservice_test/src/com/android/car/test/AudioRoutingPolicyTest.java
@@ -99,6 +99,7 @@
int contexts = v.get(VehicleAudioRoutingPolicyIndex.CONTEXTS);
// check if all contexts are allowed ones.
assertTrue((contexts & ~(
+ VehicleAudioContextFlag.RINGTONE_FLAG |
VehicleAudioContextFlag.ALARM_FLAG |
VehicleAudioContextFlag.CALL_FLAG |
VehicleAudioContextFlag.MUSIC_FLAG |
diff --git a/tests/carservice_test/src/com/android/car/test/CarDiagnosticConstantsTest.java b/tests/carservice_test/src/com/android/car/test/CarDiagnosticConstantsTest.java
new file mode 100644
index 0000000..2ed2658
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/test/CarDiagnosticConstantsTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.test;
+
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+/**
+ * Validates that diagnostic constants in CarService and Vehicle HAL have the same value
+ * This is an important assumption to validate because we do not perform any mapping between
+ * the two layers, instead relying on the constants on both sides having identical values.
+ */
+@MediumTest
+public class CarDiagnosticConstantsTest extends TestCase {
+ static final String TAG = CarDiagnosticConstantsTest.class.getSimpleName();
+
+ static class MismatchException extends Exception {
+ private static String dumpClass(Class<?> clazz) {
+ StringBuilder builder = new StringBuilder(clazz.getName() + "{\n");
+ Arrays.stream(clazz.getFields()).forEach((Field field) -> {
+ builder.append('\t').append(field.toString()).append('\n');
+ });
+ return builder.append('}').toString();
+ }
+
+ private static void logClasses(Class<?> clazz1, Class<?> clazz2) {
+ Log.d(TAG, "MismatchException. class1: " + dumpClass(clazz1));
+ Log.d(TAG, "MismatchException. class2: " + dumpClass(clazz2));
+ }
+
+ MismatchException(String message) {
+ super(message);
+ }
+
+ static MismatchException fieldValueMismatch(Class<?> clazz1, Class<?> clazz2, String name,
+ int value1, int value2) {
+ logClasses(clazz1, clazz2);
+ return new MismatchException("In comparison of " + clazz1 + " and " + clazz2 +
+ " field " + name + " had different values " + value1 + " vs. " + value2);
+ }
+
+ static MismatchException fieldsOnlyInClass1(Class<?> clazz1, Class<?> clazz2,
+ Map<String, Integer> fields) {
+ logClasses(clazz1, clazz2);
+ return new MismatchException("In comparison of " + clazz1 + " and " + clazz2 +
+ " some fields were only found in the first class:\n" +
+ fields.keySet().stream().reduce("",
+ (String s, String t) -> s + "\n" + t));
+ }
+
+ static MismatchException fieldOnlyInClass2(Class<?> clazz1, Class<?> clazz2, String field) {
+ logClasses(clazz1, clazz2);
+ return new MismatchException("In comparison of " + clazz1 + " and " + clazz2 +
+ " field " + field + " was not found in both classes");
+ }
+ }
+
+ static boolean isPublicStaticFinalInt(Field field) {
+ final int modifiers = field.getModifiers();
+ final boolean isPublic = (modifiers & Modifier.PUBLIC) == Modifier.PUBLIC;
+ final boolean isStatic = (modifiers & Modifier.STATIC) == Modifier.STATIC;
+ final boolean isFinal = (modifiers & Modifier.FINAL) == Modifier.FINAL;
+ if (isPublic && isStatic && isFinal) {
+ return field.getType() == int.class;
+ }
+ return false;
+ }
+
+ static void validateMatch(Class<?> clazz1, Class<?> clazz2) throws Exception {
+ Map<String, Integer> fields = new HashMap<>();
+
+ // add all the fields in the first class to a map
+ Arrays.stream(clazz1.getFields()).filter(
+ CarDiagnosticConstantsTest::isPublicStaticFinalInt).forEach( (Field field) -> {
+ final String name = field.getName();
+ try {
+ fields.put(name, field.getInt(null));
+ } catch (IllegalAccessException e) {
+ // this will practically never happen because we checked that it is a
+ // public static final field before reading from it
+ Log.wtf(TAG, String.format("attempt to access field %s threw exception",
+ field.toString()), e);
+ }
+ });
+
+ // check for all fields in the second class, and remove matches from the map
+ for (Field field2 : clazz2.getFields()) {
+ if (isPublicStaticFinalInt(field2)) {
+ final String name = field2.getName();
+ if (fields.containsKey(name)) {
+ try {
+ final int value2 = field2.getInt(null);
+ final int value1 = fields.getOrDefault(name, value2+1);
+ if (value2 != value1) {
+ throw MismatchException.fieldValueMismatch(clazz1, clazz2,
+ field2.getName(), value1, value2);
+ }
+ fields.remove(name);
+ } catch (IllegalAccessException e) {
+ // this will practically never happen because we checked that it is a
+ // public static final field before reading from it
+ Log.wtf(TAG, String.format("attempt to access field %s threw exception",
+ field2.toString()), e);
+ throw e;
+ }
+ } else {
+ throw MismatchException.fieldOnlyInClass2(clazz1, clazz2, name);
+ }
+ }
+ }
+
+ // if anything is left, we didn't find some fields in the second class
+ if (!fields.isEmpty()) {
+ throw MismatchException.fieldsOnlyInClass1(clazz1, clazz2, fields);
+ }
+ }
+
+ public void testFuelSystemStatus() throws Exception {
+ validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2FuelSystemStatus.class,
+ android.car.diagnostic.CarDiagnosticEvent.FuelSystemStatus.class);
+ }
+
+ public void testFuelType() throws Exception {
+ validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2FuelType.class,
+ android.car.diagnostic.CarDiagnosticEvent.FuelType.class);
+ }
+
+ public void testSecondaryAirStatus() throws Exception {
+ validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2SecondaryAirStatus.class,
+ android.car.diagnostic.CarDiagnosticEvent.SecondaryAirStatus.class);
+ }
+
+ public void testIgnitionMonitors() throws Exception {
+ validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2CommonIgnitionMonitors.class,
+ android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors.class);
+
+ validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2CompressionIgnitionMonitors.class,
+ android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors.class);
+
+ validateMatch(android.hardware.automotive.vehicle.V2_0.Obd2SparkIgnitionMonitors.class,
+ android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors.class);
+ }
+}
diff --git a/tests/carservice_test/src/com/android/car/test/CarDiagnosticManagerTest.java b/tests/carservice_test/src/com/android/car/test/CarDiagnosticManagerTest.java
index d316ba6..b77a844 100644
--- a/tests/carservice_test/src/com/android/car/test/CarDiagnosticManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarDiagnosticManagerTest.java
@@ -19,24 +19,23 @@
import static java.lang.Integer.toHexString;
import android.car.Car;
-import android.car.hardware.CarDiagnosticEvent;
-import android.car.hardware.CarDiagnosticEvent.FuelSystemStatus;
-import android.car.hardware.CarDiagnosticEvent.FuelType;
-import android.car.hardware.CarDiagnosticEvent.IgnitionMonitors.CommonIgnitionMonitors;
-import android.car.hardware.CarDiagnosticEvent.IgnitionMonitors.CompressionIgnitionMonitors;
-import android.car.hardware.CarDiagnosticEvent.IgnitionMonitors.SparkIgnitionMonitors;
-import android.car.hardware.CarDiagnosticEvent.SecondaryAirStatus;
-import android.car.hardware.CarDiagnosticManager;
-import android.car.hardware.CarDiagnosticSensorIndices.Obd2FloatSensorIndex;
-import android.car.hardware.CarDiagnosticSensorIndices.Obd2IntegerSensorIndex;
+import android.car.diagnostic.CarDiagnosticEvent;
+import android.car.diagnostic.CarDiagnosticEvent.FuelSystemStatus;
+import android.car.diagnostic.CarDiagnosticEvent.FuelType;
+import android.car.diagnostic.CarDiagnosticEvent.CommonIgnitionMonitors;
+import android.car.diagnostic.CarDiagnosticEvent.CompressionIgnitionMonitors;
+import android.car.diagnostic.CarDiagnosticEvent.SparkIgnitionMonitors;
+import android.car.diagnostic.CarDiagnosticEvent.SecondaryAirStatus;
+import android.car.diagnostic.CarDiagnosticManager;
+import android.car.diagnostic.FloatSensorIndex;
+import android.car.diagnostic.IntegerSensorIndex;
import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
-import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
import android.os.SystemClock;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.JsonReader;
import android.util.JsonWriter;
import android.util.Log;
-import com.android.car.internal.FeatureConfiguration;
import com.android.car.vehiclehal.DiagnosticEventBuilder;
import com.android.car.vehiclehal.DiagnosticJson;
import com.android.car.vehiclehal.VehiclePropValueBuilder;
@@ -205,23 +204,23 @@
@Override
protected void setUp() throws Exception {
- mLiveFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.AMBIENT_AIR_TEMPERATURE, 30);
+ mLiveFrameEventBuilder.addIntSensor(IntegerSensorIndex.AMBIENT_AIR_TEMPERATURE, 30);
mLiveFrameEventBuilder.addIntSensor(
- Obd2IntegerSensorIndex.FUEL_SYSTEM_STATUS,
+ IntegerSensorIndex.FUEL_SYSTEM_STATUS,
FuelSystemStatus.OPEN_ENGINE_LOAD_OR_DECELERATION);
mLiveFrameEventBuilder.addIntSensor(
- Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START, 5000);
- mLiveFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.CONTROL_MODULE_VOLTAGE, 2);
- mLiveFrameEventBuilder.addFloatSensor(Obd2FloatSensorIndex.CALCULATED_ENGINE_LOAD, 0.125f);
- mLiveFrameEventBuilder.addFloatSensor(Obd2FloatSensorIndex.VEHICLE_SPEED, 12.5f);
+ IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START, 5000);
+ mLiveFrameEventBuilder.addIntSensor(IntegerSensorIndex.CONTROL_MODULE_VOLTAGE, 2);
+ mLiveFrameEventBuilder.addFloatSensor(FloatSensorIndex.CALCULATED_ENGINE_LOAD, 0.125f);
+ mLiveFrameEventBuilder.addFloatSensor(FloatSensorIndex.VEHICLE_SPEED, 12.5f);
- mFreezeFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.AMBIENT_AIR_TEMPERATURE, 30);
+ mFreezeFrameEventBuilder.addIntSensor(IntegerSensorIndex.AMBIENT_AIR_TEMPERATURE, 30);
mFreezeFrameEventBuilder.addIntSensor(
- Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START, 5000);
- mFreezeFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.CONTROL_MODULE_VOLTAGE, 2);
+ IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START, 5000);
+ mFreezeFrameEventBuilder.addIntSensor(IntegerSensorIndex.CONTROL_MODULE_VOLTAGE, 2);
mFreezeFrameEventBuilder.addFloatSensor(
- Obd2FloatSensorIndex.CALCULATED_ENGINE_LOAD, 0.125f);
- mFreezeFrameEventBuilder.addFloatSensor(Obd2FloatSensorIndex.VEHICLE_SPEED, 12.5f);
+ FloatSensorIndex.CALCULATED_ENGINE_LOAD, 0.125f);
+ mFreezeFrameEventBuilder.addFloatSensor(FloatSensorIndex.VEHICLE_SPEED, 12.5f);
mFreezeFrameEventBuilder.setDTC(DTC);
super.setUp();
@@ -242,26 +241,26 @@
assertEquals(
5000,
liveFrame
- .getSystemIntegerSensor(Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
+ .getSystemIntegerSensor(IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
.intValue());
assertEquals(
30,
liveFrame
- .getSystemIntegerSensor(Obd2IntegerSensorIndex.AMBIENT_AIR_TEMPERATURE)
+ .getSystemIntegerSensor(IntegerSensorIndex.AMBIENT_AIR_TEMPERATURE)
.intValue());
assertEquals(
2,
liveFrame
- .getSystemIntegerSensor(Obd2IntegerSensorIndex.CONTROL_MODULE_VOLTAGE)
+ .getSystemIntegerSensor(IntegerSensorIndex.CONTROL_MODULE_VOLTAGE)
.intValue());
assertEquals(
0.125f,
liveFrame
- .getSystemFloatSensor(Obd2FloatSensorIndex.CALCULATED_ENGINE_LOAD)
+ .getSystemFloatSensor(FloatSensorIndex.CALCULATED_ENGINE_LOAD)
.floatValue());
assertEquals(
12.5f,
- liveFrame.getSystemFloatSensor(Obd2FloatSensorIndex.VEHICLE_SPEED).floatValue());
+ liveFrame.getSystemFloatSensor(FloatSensorIndex.VEHICLE_SPEED).floatValue());
}
public void testLiveFrameEvent() throws Exception {
@@ -274,7 +273,7 @@
listener.reset();
long time = SystemClock.elapsedRealtimeNanos();
mLiveFrameEventBuilder.addIntSensor(
- Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START, 5100);
+ IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START, 5100);
getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build(time));
assertTrue(listener.waitForEvent(time));
@@ -284,7 +283,7 @@
assertEquals(
5100,
liveFrame
- .getSystemIntegerSensor(Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
+ .getSystemIntegerSensor(IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
.intValue());
}
@@ -303,23 +302,23 @@
assertNull(
liveFrame.getSystemIntegerSensor(
- Obd2IntegerSensorIndex.DRIVER_DEMAND_PERCENT_TORQUE));
+ IntegerSensorIndex.DRIVER_DEMAND_PERCENT_TORQUE));
assertEquals(
-1,
liveFrame.getSystemIntegerSensor(
- Obd2IntegerSensorIndex.DRIVER_DEMAND_PERCENT_TORQUE, -1));
+ IntegerSensorIndex.DRIVER_DEMAND_PERCENT_TORQUE, -1));
- assertNull(liveFrame.getSystemFloatSensor(Obd2FloatSensorIndex.OXYGEN_SENSOR6_VOLTAGE));
+ assertNull(liveFrame.getSystemFloatSensor(FloatSensorIndex.OXYGEN_SENSOR6_VOLTAGE));
assertEquals(
0.25f,
- liveFrame.getSystemFloatSensor(Obd2FloatSensorIndex.OXYGEN_SENSOR5_VOLTAGE, 0.25f));
+ liveFrame.getSystemFloatSensor(FloatSensorIndex.OXYGEN_SENSOR5_VOLTAGE, 0.25f));
- assertNull(liveFrame.getVendorIntegerSensor(Obd2IntegerSensorIndex.VENDOR_START));
- assertEquals(-1, liveFrame.getVendorIntegerSensor(Obd2IntegerSensorIndex.VENDOR_START, -1));
+ assertNull(liveFrame.getVendorIntegerSensor(IntegerSensorIndex.VENDOR_START));
+ assertEquals(-1, liveFrame.getVendorIntegerSensor(IntegerSensorIndex.VENDOR_START, -1));
- assertNull(liveFrame.getVendorFloatSensor(Obd2FloatSensorIndex.VENDOR_START));
+ assertNull(liveFrame.getVendorFloatSensor(FloatSensorIndex.VENDOR_START));
assertEquals(
- 0.25f, liveFrame.getVendorFloatSensor(Obd2FloatSensorIndex.VENDOR_START, 0.25f));
+ 0.25f, liveFrame.getVendorFloatSensor(FloatSensorIndex.VENDOR_START, 0.25f));
}
public void testFuelSystemStatus() throws Exception {
@@ -338,7 +337,7 @@
assertEquals(
FuelSystemStatus.OPEN_ENGINE_LOAD_OR_DECELERATION,
liveFrame
- .getSystemIntegerSensor(Obd2IntegerSensorIndex.FUEL_SYSTEM_STATUS)
+ .getSystemIntegerSensor(IntegerSensorIndex.FUEL_SYSTEM_STATUS)
.intValue());
assertEquals(
FuelSystemStatus.OPEN_ENGINE_LOAD_OR_DECELERATION,
@@ -353,7 +352,7 @@
android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
mLiveFrameEventBuilder.addIntSensor(
- Obd2IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS,
+ IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS,
SecondaryAirStatus.FROM_OUTSIDE_OR_OFF);
long timestamp = SystemClock.elapsedRealtimeNanos();
getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build(timestamp));
@@ -367,7 +366,7 @@
SecondaryAirStatus.FROM_OUTSIDE_OR_OFF,
liveFrame
.getSystemIntegerSensor(
- Obd2IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS)
+ IntegerSensorIndex.COMMANDED_SECONDARY_AIR_STATUS)
.intValue());
assertEquals(
SecondaryAirStatus.FROM_OUTSIDE_OR_OFF,
@@ -388,9 +387,9 @@
final int compressionMonitorsValue =
(0x1 << 2) | (0x1 << 3) | (0x1 << 6) | (0x1 << 12) | (0x1 << 13);
- mLiveFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED, 0);
+ mLiveFrameEventBuilder.addIntSensor(IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED, 0);
mLiveFrameEventBuilder.addIntSensor(
- Obd2IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS, sparkMonitorsValue);
+ IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS, sparkMonitorsValue);
long timestamp = SystemClock.elapsedRealtimeNanos();
getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build(timestamp));
@@ -431,9 +430,9 @@
assertFalse(sparkIgnitionMonitors.catalyst.available);
assertFalse(sparkIgnitionMonitors.catalyst.incomplete);
- mLiveFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED, 1);
+ mLiveFrameEventBuilder.addIntSensor(IntegerSensorIndex.IGNITION_MONITORS_SUPPORTED, 1);
mLiveFrameEventBuilder.addIntSensor(
- Obd2IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS, compressionMonitorsValue);
+ IntegerSensorIndex.IGNITION_SPECIFIC_MONITORS, compressionMonitorsValue);
timestamp += 1000;
getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build(timestamp));
@@ -479,7 +478,7 @@
android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
mLiveFrameEventBuilder.addIntSensor(
- Obd2IntegerSensorIndex.FUEL_TYPE, FuelType.BIFUEL_RUNNING_LPG);
+ IntegerSensorIndex.FUEL_TYPE, FuelType.BIFUEL_RUNNING_LPG);
long timestamp = SystemClock.elapsedRealtimeNanos();
getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build(timestamp));
@@ -490,7 +489,7 @@
assertEquals(
FuelType.BIFUEL_RUNNING_LPG,
- liveFrame.getSystemIntegerSensor(Obd2IntegerSensorIndex.FUEL_TYPE).intValue());
+ liveFrame.getSystemIntegerSensor(IntegerSensorIndex.FUEL_TYPE).intValue());
assertEquals(FuelType.BIFUEL_RUNNING_LPG, liveFrame.getFuelType().intValue());
}
@@ -501,8 +500,8 @@
CarDiagnosticManager.FRAME_TYPE_LIVE,
android.car.hardware.CarSensorManager.SENSOR_RATE_NORMAL);
- mLiveFrameEventBuilder.addIntSensor(Obd2IntegerSensorIndex.ENGINE_OIL_TEMPERATURE, 74);
- mLiveFrameEventBuilder.addFloatSensor(Obd2FloatSensorIndex.OXYGEN_SENSOR1_VOLTAGE, 0.125f);
+ mLiveFrameEventBuilder.addIntSensor(IntegerSensorIndex.ENGINE_OIL_TEMPERATURE, 74);
+ mLiveFrameEventBuilder.addFloatSensor(FloatSensorIndex.OXYGEN_SENSOR1_VOLTAGE, 0.125f);
long timestamp = SystemClock.elapsedRealtimeNanos();
getMockedVehicleHal().injectEvent(mLiveFrameEventBuilder.build(timestamp));
@@ -515,11 +514,11 @@
assertEquals(
74,
liveFrame
- .getSystemIntegerSensor(Obd2IntegerSensorIndex.ENGINE_OIL_TEMPERATURE)
+ .getSystemIntegerSensor(IntegerSensorIndex.ENGINE_OIL_TEMPERATURE)
.intValue());
assertEquals(
0.125f,
- liveFrame.getSystemFloatSensor(Obd2FloatSensorIndex.OXYGEN_SENSOR1_VOLTAGE));
+ liveFrame.getSystemFloatSensor(FloatSensorIndex.OXYGEN_SENSOR1_VOLTAGE));
StringWriter stringWriter = new StringWriter();
JsonWriter jsonWriter = new JsonWriter(stringWriter);
@@ -535,11 +534,11 @@
74,
diagnosticJson
.intValues
- .get(Obd2IntegerSensorIndex.ENGINE_OIL_TEMPERATURE)
+ .get(IntegerSensorIndex.ENGINE_OIL_TEMPERATURE)
.intValue());
assertEquals(
0.125f,
- diagnosticJson.floatValues.get(Obd2FloatSensorIndex.OXYGEN_SENSOR1_VOLTAGE));
+ diagnosticJson.floatValues.get(FloatSensorIndex.OXYGEN_SENSOR1_VOLTAGE));
}
public void testMultipleListeners() throws Exception {
@@ -576,11 +575,11 @@
assertEquals(
5000,
- event1.getSystemIntegerSensor(Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
+ event1.getSystemIntegerSensor(IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
.intValue());
assertEquals(
5000,
- event2.getSystemIntegerSensor(Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
+ event2.getSystemIntegerSensor(IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
.intValue());
listener1.reset();
@@ -602,7 +601,7 @@
assertEquals(
5000,
- event2.getSystemIntegerSensor(Obd2IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
+ event2.getSystemIntegerSensor(IntegerSensorIndex.RUNTIME_SINCE_ENGINE_START)
.intValue());
}
@@ -624,7 +623,7 @@
assertEquals(DTC, freezeFrame.dtc);
mFreezeFrameEventBuilder.addIntSensor(
- Obd2IntegerSensorIndex.ABSOLUTE_BAROMETRIC_PRESSURE, 22);
+ IntegerSensorIndex.ABSOLUTE_BAROMETRIC_PRESSURE, 22);
injectedEvent = mFreezeFrameProperties.addNewEvent(mFreezeFrameEventBuilder);
getMockedVehicleHal().injectEvent(injectedEvent);
assertTrue(listener.waitForEvent(injectedEvent.timestamp));
@@ -640,7 +639,7 @@
assertEquals(
22,
freezeFrame
- .getSystemIntegerSensor(Obd2IntegerSensorIndex.ABSOLUTE_BAROMETRIC_PRESSURE)
+ .getSystemIntegerSensor(IntegerSensorIndex.ABSOLUTE_BAROMETRIC_PRESSURE)
.intValue());
}
@@ -730,9 +729,9 @@
public void testIsSupportedApiCalls() throws Exception {
assertTrue(mCarDiagnosticManager.isLiveFrameSupported());
- assertTrue(mCarDiagnosticManager.isFreezeFrameSupported());
- assertTrue(mCarDiagnosticManager.isFreezeFrameTimestampSupported());
- assertTrue(mCarDiagnosticManager.isFreezeFrameClearSupported());
+ assertTrue(mCarDiagnosticManager.isFreezeFrameNotificationSupported());
+ assertTrue(mCarDiagnosticManager.isGetFreezeFrameSupported());
+ assertTrue(mCarDiagnosticManager.isClearFreezeFramesSupported());
}
class Listener implements CarDiagnosticManager.OnDiagnosticEventListener {
diff --git a/tests/carservice_test/src/com/android/car/test/CarInfoManagerTest.java b/tests/carservice_test/src/com/android/car/test/CarInfoManagerTest.java
index c3f6857..2cdbbb0 100644
--- a/tests/carservice_test/src/com/android/car/test/CarInfoManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarInfoManagerTest.java
@@ -18,9 +18,11 @@
import android.car.Car;
import android.car.CarInfoManager;
import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.test.suitebuilder.annotation.MediumTest;
import com.android.car.vehiclehal.VehiclePropValueBuilder;
+@MediumTest
public class CarInfoManagerTest extends MockedCarTestBase {
private static final String MAKE_NAME = "ANDROID";
diff --git a/tests/carservice_test/src/com/android/car/test/CarProjectionManagerTest.java b/tests/carservice_test/src/com/android/car/test/CarProjectionManagerTest.java
index 64f5fb8..adf288c 100644
--- a/tests/carservice_test/src/com/android/car/test/CarProjectionManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarProjectionManagerTest.java
@@ -68,7 +68,7 @@
}
public void testShortPressListener() throws Exception {
- mManager.regsiterProjectionListener(
+ mManager.registerProjectionListener(
mListener,
CarProjectionManager.PROJECTION_VOICE_SEARCH);
assertEquals(0, mAvailable.availablePermits());
@@ -79,7 +79,7 @@
}
public void testLongPressListener() throws Exception {
- mManager.regsiterProjectionListener(
+ mManager.registerProjectionListener(
mListener,
CarProjectionManager.PROJECTION_LONG_PRESS_VOICE_SEARCH);
assertEquals(0, mLongAvailable.availablePermits());
@@ -90,7 +90,7 @@
}
public void testMixedPressListener() throws Exception {
- mManager.regsiterProjectionListener(
+ mManager.registerProjectionListener(
mListener,
CarProjectionManager.PROJECTION_LONG_PRESS_VOICE_SEARCH
| CarProjectionManager.PROJECTION_VOICE_SEARCH);
diff --git a/tests/carservice_test/src/com/android/car/test/CarVendorExtensionManagerTest.java b/tests/carservice_test/src/com/android/car/test/CarVendorExtensionManagerTest.java
index ec367b8..fa33e56 100644
--- a/tests/carservice_test/src/com/android/car/test/CarVendorExtensionManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarVendorExtensionManagerTest.java
@@ -29,6 +29,7 @@
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyGroup;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyType;
import android.os.SystemClock;
+import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
import android.util.SparseArray;
@@ -44,6 +45,7 @@
/**
* Tests for {@link CarVendorExtensionManager}
*/
+@MediumTest
public class CarVendorExtensionManagerTest extends MockedCarTestBase {
private static final String TAG = CarVendorExtensionManager.class.getSimpleName();
@@ -159,7 +161,7 @@
public void testLargeByteArrayProperty() throws Exception {
// Allocate array of byte which is greater than binder transaction buffer limitation.
- byte[] expectedData = new byte[3 * MILLION];
+ byte[] expectedData = new byte[2 * MILLION];
new Random(SystemClock.elapsedRealtimeNanos())
.nextBytes(expectedData);
@@ -252,7 +254,8 @@
result.areaId = requestedPropValue.areaId;
if (requestedPropValue.prop == CUSTOM_BYTES_PROP_ID_2 && mBytes != null) {
- Log.d(TAG, "Returning byte array property, value: " + Arrays.toString(mBytes));
+ Log.d(TAG, "Returning byte array property, value size " + mBytes.length);
+ result.value.bytes.ensureCapacity(mBytes.length);
for (byte b : mBytes) {
result.value.bytes.add(b);
}
diff --git a/tests/carservice_test/src/com/android/car/test/CarVolumeServiceTest.java b/tests/carservice_test/src/com/android/car/test/CarVolumeServiceTest.java
index df2b532..2e92a59 100644
--- a/tests/carservice_test/src/com/android/car/test/CarVolumeServiceTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarVolumeServiceTest.java
@@ -17,12 +17,11 @@
import static com.android.car.test.AudioTestUtils.doRequestFocus;
-import com.google.android.collect.Lists;
-
import android.car.Car;
import android.car.CarNotConnectedException;
import android.car.media.CarAudioManager;
import android.content.Context;
+import android.hardware.automotive.vehicle.V2_0.VehicleAudioContextFlag;
import android.hardware.automotive.vehicle.V2_0.VehicleAudioExtFocusFlag;
import android.hardware.automotive.vehicle.V2_0.VehicleAudioFocusState;
import android.hardware.automotive.vehicle.V2_0.VehicleAudioVolumeIndex;
@@ -35,19 +34,23 @@
import android.media.IVolumeController;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
import android.util.Pair;
import android.util.SparseIntArray;
import android.view.KeyEvent;
+import com.android.car.VolumeUtils;
import com.android.car.vehiclehal.VehiclePropValueBuilder;
import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
import com.android.internal.annotations.GuardedBy;
+import com.google.android.collect.Lists;
+
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
+@MediumTest
public class CarVolumeServiceTest extends MockedCarTestBase {
private static final String TAG = CarVolumeServiceTest.class.getSimpleName();
@@ -79,24 +82,65 @@
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
}
});
+ }
+ private SingleChannelVolumeHandler setupExternalVolumeEmulation(boolean supportAudioContext)
+ throws Exception {
List<Integer> maxs = new ArrayList<>();
- maxs.add(MAX_VOL);
- maxs.add(MAX_VOL);
-
- // TODO: add tests for audio context supported cases.
- startVolumeEmulation(0 /*supported audio context*/, maxs);
+ int supportedAudioContext = 0;
+ if (!supportAudioContext) {
+ // set up 2 physical streams
+ maxs.add(MAX_VOL);
+ maxs.add(MAX_VOL);
+ } else {
+ // add supported contexts
+ int[] contexts = VolumeUtils.CAR_AUDIO_CONTEXT;
+ for (int context : contexts) {
+ supportedAudioContext |= context;
+ maxs.add(MAX_VOL);
+ }
+ }
+ SingleChannelVolumeHandler handler =
+ startVolumeEmulation(supportedAudioContext, maxs);
mCarAudioManager = (CarAudioManager) getCar().getCarManager(Car.AUDIO_SERVICE);
+ return handler;
+ }
+
+ public void testUnknownVolumeChange() throws Exception {
+ SingleChannelVolumeHandler volumeHandler = setupExternalVolumeEmulation(true);
+ VolumeController volumeController = new VolumeController();
+ mCarAudioManager.setVolumeController(volumeController);
+ mCarAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 2, 0);
+ // give focus to music, now current context becomes VehicleAudioContextFlag.MUSIC_FLAG
+ CarAudioFocusTest.AudioFocusListener listenerMusic =
+ new CarAudioFocusTest.AudioFocusListener();
+ int res = doRequestFocus(mAudioManager, listenerMusic,
+ AudioManager.STREAM_MUSIC,
+ AudioManager.AUDIOFOCUS_GAIN);
+ assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
+ int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.STATE_GAIN,
+ request[1],
+ VehicleAudioExtFocusFlag.NONE_FLAG);
+
+ // let vehicle hal report volume change from unknown context, we should map it to the
+ // current context (music).
+ volumeHandler.injectVolumeEvent(VehicleAudioContextFlag.UNKNOWN_FLAG, 3);
+ // now music volume should be recorded as 3.
+ volumeVerificationPoll(createStreamVolPair(AudioManager.STREAM_MUSIC, 3));
}
public void testVolumeLimits() throws Exception {
+ setupExternalVolumeEmulation(false);
for (int stream : LOGICAL_STREAMS) {
assertEquals(MAX_VOL, mCarAudioManager.getStreamMaxVolume(stream));
}
}
- public void testVolumeSet() {
+ public void testVolumeSet() throws Exception {
try {
+ setupExternalVolumeEmulation(false);
int callVol = 10;
int musicVol = 15;
mCarAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, musicVol, 0);
@@ -117,6 +161,7 @@
public void testSuppressVolumeUI() {
try {
+ setupExternalVolumeEmulation(false);
VolumeController volumeController = new VolumeController();
mCarAudioManager.setVolumeController(volumeController);
@@ -164,6 +209,7 @@
public void testVolumeKeys() throws Exception {
try {
+ setupExternalVolumeEmulation(false);
int musicVol = 10;
mCarAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, musicVol, 0);
int callVol = 12;
@@ -270,12 +316,21 @@
}
}
+ public void injectVolumeEvent(int context, int volume) {
+ getMockedVehicleHal().injectEvent(
+ VehiclePropValueBuilder.newBuilder(VehicleProperty.AUDIO_VOLUME)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .addIntValue(context, volume, 0)
+ .build());
+ }
+
@Override
public void onPropertySet(VehiclePropValue value) {
ArrayList<Integer> v = value.value.int32Values;
- int stream = v.get(VehicleAudioVolumeIndex.INDEX_STREAM);
- int volume = v.get(VehicleAudioVolumeIndex.INDEX_VOLUME);
- int state = v.get(VehicleAudioVolumeIndex.INDEX_STATE);
+ int stream = v.get(VehicleAudioVolumeIndex.STREAM);
+ int volume = v.get(VehicleAudioVolumeIndex.VOLUME);
+ int state = v.get(VehicleAudioVolumeIndex.STATE);
+ Log.d(TAG, "state " + state);
mCurrent.put(stream, volume);
getMockedVehicleHal().injectEvent(
@@ -287,7 +342,7 @@
@Override
public VehiclePropValue onPropertyGet(VehiclePropValue value) {
- int stream = value.value.int32Values.get(VehicleAudioVolumeIndex.INDEX_STREAM);
+ int stream = value.value.int32Values.get(VehicleAudioVolumeIndex.STREAM);
int volume = mCurrent.get(stream);
return VehiclePropValueBuilder.newBuilder(VehicleProperty.AUDIO_VOLUME)
.setTimestamp(SystemClock.elapsedRealtimeNanos())
@@ -356,7 +411,8 @@
}
};
- private void startVolumeEmulation(int supportedAudioVolumeContext, List<Integer> maxs) {
+ private SingleChannelVolumeHandler startVolumeEmulation(int supportedAudioVolumeContext,
+ List<Integer> maxs) {
SingleChannelVolumeHandler singleChannelVolumeHandler =
new SingleChannelVolumeHandler(maxs);
int zones = (1<<maxs.size()) - 1;
@@ -388,6 +444,7 @@
.setConfigArray(Lists.newArrayList(0));
reinitializeMockedHal();
+ return singleChannelVolumeHandler;
}
private void sendVolumeKey(boolean volUp) {
diff --git a/tests/carservice_test/src/com/android/car/test/SimpleVmsPublisherClientService.java b/tests/carservice_test/src/com/android/car/test/SimpleVmsPublisherClientService.java
index c8badf2..c3c15f5 100644
--- a/tests/carservice_test/src/com/android/car/test/SimpleVmsPublisherClientService.java
+++ b/tests/carservice_test/src/com/android/car/test/SimpleVmsPublisherClientService.java
@@ -34,6 +34,7 @@
public void onVmsPublisherServiceReady() {
// Publish a property that is going to be verified in the test.
publish(VmsPublisherClientServiceTest.MOCK_PUBLISHER_LAYER,
+ VmsPublisherClientServiceTest.MOCK_PUBLISHER_ID,
VmsPublisherClientServiceTest.PAYLOAD);
}
}
diff --git a/tests/carservice_test/src/com/android/car/test/SystemActivityMonitoringServiceTest.java b/tests/carservice_test/src/com/android/car/test/SystemActivityMonitoringServiceTest.java
index 90598eb..3345605 100644
--- a/tests/carservice_test/src/com/android/car/test/SystemActivityMonitoringServiceTest.java
+++ b/tests/carservice_test/src/com/android/car/test/SystemActivityMonitoringServiceTest.java
@@ -23,6 +23,7 @@
import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
import android.os.SystemClock;
+import android.test.suitebuilder.annotation.MediumTest;
import com.android.car.SystemActivityMonitoringService;
import com.android.car.SystemActivityMonitoringService.TopTaskInfoContainer;
@@ -34,6 +35,7 @@
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
+@MediumTest
public class SystemActivityMonitoringServiceTest extends MockedCarTestBase {
private static final long TIMEOUT_MS = 3000;
private static final long POLL_INTERVAL_MS = 50;
@@ -52,6 +54,11 @@
// blocking activity.
mDrivingStatusHandler.setDrivingStatusRestricted(drivingStatusRestricted);
+ // Due to asynchronous nature of Car Service initialization, if we won't wait we may inject
+ // an event while SensorHalService is not subscribed yet.
+ assertTrue(getMockedVehicleHal()
+ .waitForSubscriber(VehicleProperty.DRIVING_STATUS, TIMEOUT_MS));
+
VehiclePropValue injectValue =
VehiclePropValueBuilder.newBuilder(VehicleProperty.DRIVING_STATUS)
.setTimestamp(SystemClock.elapsedRealtimeNanos())
diff --git a/tests/carservice_test/src/com/android/car/test/VmsHalServiceSubscriptionEventTest.java b/tests/carservice_test/src/com/android/car/test/VmsHalServiceSubscriptionEventTest.java
index 240598a..cad3086 100644
--- a/tests/carservice_test/src/com/android/car/test/VmsHalServiceSubscriptionEventTest.java
+++ b/tests/carservice_test/src/com/android/car/test/VmsHalServiceSubscriptionEventTest.java
@@ -24,10 +24,10 @@
import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
-import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
-import android.hardware.automotive.vehicle.V2_1.VmsSimpleMessageIntegerValuesIndex;
-import android.hardware.automotive.vehicle.V2_1.VmsMessageType;
-import android.hardware.automotive.vehicle.V2_1.VmsSubscriptionResponseFormat;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_0.VmsMessageWithLayerIntegerValuesIndex;
+import android.hardware.automotive.vehicle.V2_0.VmsMessageType;
+import android.hardware.automotive.vehicle.V2_0.VmsSubscriptionsStateIntegerValuesIndex;
import android.test.suitebuilder.annotation.MediumTest;
import com.android.car.vehiclehal.VehiclePropValueBuilder;
@@ -82,18 +82,18 @@
public void testOneSubscription() throws Exception {
if (!VmsTestUtils.canRunTest(TAG)) return;
- List<VmsLayer> layers = Arrays.asList(new VmsLayer(8, 3));
+ List<VmsLayer> layers = Arrays.asList(new VmsLayer(8, 0, 3));
subscriptionTestLogic(layers);
}
public void testManySubscriptions() throws Exception {
if (!VmsTestUtils.canRunTest(TAG)) return;
List<VmsLayer> layers = Arrays.asList(
- new VmsLayer(8, 3),
- new VmsLayer(5, 1),
- new VmsLayer(3, 9),
- new VmsLayer(2, 7),
- new VmsLayer(9, 3));
+ new VmsLayer(8, 1, 3),
+ new VmsLayer(5, 2, 1),
+ new VmsLayer(3, 3, 9),
+ new VmsLayer(2, 4, 7),
+ new VmsLayer(9, 5, 3));
subscriptionTestLogic(layers);
}
@@ -111,19 +111,21 @@
assertTrue(mHalHandlerSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
// Validate response.
ArrayList<Integer> v = mHalHandler.getValues();
- int messageType = v.get(VmsSubscriptionResponseFormat.VMS_MESSAGE_TYPE);
- int sequenceNumber = v.get(VmsSubscriptionResponseFormat.SEQUENCE_NUMBER);
- int numberLayers = v.get(VmsSubscriptionResponseFormat.NUMBER_OF_LAYERS);
- assertEquals(VmsMessageType.SUBSCRIPTION_RESPONSE, messageType);
- assertEquals(layers.size(), sequenceNumber);
+ int messageType = v.get(VmsSubscriptionsStateIntegerValuesIndex.MESSAGE_TYPE);
+ int sequenceNumber = v.get(VmsSubscriptionsStateIntegerValuesIndex.SEQUENCE_NUMBER);
+ int numberLayers = v.get(VmsSubscriptionsStateIntegerValuesIndex.NUMBER_OF_LAYERS);
+ assertEquals(VmsMessageType.SUBSCRIPTIONS_RESPONSE, messageType);
+ //TODO(asafro): This assertion makes no sense. need to fix.
+ //assertEquals(layers.size(), sequenceNumber);
assertEquals(layers.size(), numberLayers);
List<VmsLayer> receivedLayers = new ArrayList<>();
- int start = VmsSubscriptionResponseFormat.FIRST_LAYER;
- int end = VmsSubscriptionResponseFormat.FIRST_LAYER + 2 * numberLayers;
+ int start = VmsSubscriptionsStateIntegerValuesIndex.SUBSCRIPTIONS_START;
+ int end = VmsSubscriptionsStateIntegerValuesIndex.SUBSCRIPTIONS_START + 3 * numberLayers;
while (start < end) {
- int id = v.get(start++);
+ int type = v.get(start++);
+ int subtype = v.get(start++);
int version = v.get(start++);
- receivedLayers.add(new VmsLayer(id, version));
+ receivedLayers.add(new VmsLayer(type, subtype, version));
}
assertEquals(new HashSet<>(layers), new HashSet<>(receivedLayers));
}
@@ -139,25 +141,27 @@
assertTrue(mHalHandlerSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
// Validate response.
ArrayList<Integer> v = mHalHandler.getValues();
- int messsageType = v.get(VmsSimpleMessageIntegerValuesIndex.VMS_MESSAGE_TYPE);
- int layerId = v.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_ID);
- int layerVersion = v.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_VERSION);
+ int messsageType = v.get(VmsMessageWithLayerIntegerValuesIndex.MESSAGE_TYPE);
+ int layerId = v.get(VmsMessageWithLayerIntegerValuesIndex.LAYER_TYPE);
+ int layerVersion = v.get(VmsMessageWithLayerIntegerValuesIndex.LAYER_VERSION);
+ int fused = v.get(VmsMessageWithLayerIntegerValuesIndex.LAYER_SUBTYPE);
assertEquals(VmsMessageType.SUBSCRIBE, messsageType);
- assertEquals(layer.getId(), layerId);
+ assertEquals(layer.getType(), layerId);
assertEquals(layer.getVersion(), layerVersion);
}
private VehiclePropValue createHalSubscribeRequest(VmsLayer layer) {
return VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
.addIntValue(VmsMessageType.SUBSCRIBE)
- .addIntValue(layer.getId())
+ .addIntValue(layer.getType())
+ .addIntValue(layer.getSubtype())
.addIntValue(layer.getVersion())
.build();
}
private VehiclePropValue createHalSubscriptionRequest() {
return VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
- .addIntValue(VmsMessageType.SUBSCRIPTION_REQUEST)
+ .addIntValue(VmsMessageType.SUBSCRIPTIONS_REQUEST)
.build();
}
@@ -174,4 +178,4 @@
return mValues;
}
}
-}
+}
\ No newline at end of file
diff --git a/tests/carservice_test/src/com/android/car/test/VmsOperationRecorderTest.java b/tests/carservice_test/src/com/android/car/test/VmsOperationRecorderTest.java
new file mode 100644
index 0000000..179a194
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/test/VmsOperationRecorderTest.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.test;
+
+import android.car.vms.VmsLayer;
+import android.car.vms.VmsLayerDependency;
+import android.car.vms.VmsLayersOffering;
+import android.car.vms.VmsOperationRecorder;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
+
+import junit.framework.TestCase;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+
+@MediumTest
+public class VmsOperationRecorderTest extends TestCase {
+
+ /**
+ * Capture messages that VmsOperationRecorder.Writer would normally pass to Log.d(...).
+ */
+ class TestWriter extends VmsOperationRecorder.Writer {
+ public String mMsg;
+
+ @Override
+ public boolean isEnabled() {
+ return true;
+ }
+
+ @Override
+ public void write(String msg) {
+ super.write(msg);
+ mMsg = msg;
+ }
+ }
+
+ private TestWriter mWriter;
+ private VmsOperationRecorder mRecorder;
+ private static final String TAG = "VmsOperationRecorderTest";
+
+ private static final VmsLayer layer1 = new VmsLayer(1, 3, 2);
+ private static final VmsLayer layer2 = new VmsLayer(2, 4, 3);
+ private static final VmsLayer layer3 = new VmsLayer(3, 5, 4);
+
+ private static final VmsLayerDependency layerDependency1 = new VmsLayerDependency(layer3);
+ private static final VmsLayerDependency layerDependency2 = new VmsLayerDependency(layer1,
+ new HashSet<VmsLayer>(Arrays.asList(layer2, layer3)));
+
+ private static final VmsLayersOffering layersOffering0 = new VmsLayersOffering(
+ new HashSet<VmsLayerDependency>(), 66);
+ private static final VmsLayersOffering layersOffering1 = new VmsLayersOffering(
+ new HashSet<>(Arrays.asList(layerDependency1)), 66);
+ private static final VmsLayersOffering layersOffering2 = new VmsLayersOffering(
+ new HashSet<>(Arrays.asList(layerDependency1, layerDependency2)), 66);
+
+ public void setUp() {
+ mWriter = new TestWriter();
+ mRecorder = new VmsOperationRecorder(mWriter);
+ }
+
+ public void testSubscribe() throws Exception {
+ mRecorder.subscribe(layer1);
+ assertJsonMsgEquals("{'subscribe':{'layer':{'subtype':3,'type':1,'version':2}}}");
+ }
+
+ public void testUnsubscribe() throws Exception {
+ mRecorder.unsubscribe(layer1);
+ assertJsonMsgEquals("{'unsubscribe':{'layer':{'type':1,'subtype':3,'version':2}}}");
+ }
+
+ public void testStartMonitoring() throws Exception {
+ mRecorder.startMonitoring();
+ assertJsonMsgEquals("{'startMonitoring':{}}");
+ }
+
+ public void testStopMonitoring() throws Exception {
+ mRecorder.stopMonitoring();
+ assertJsonMsgEquals("{'stopMonitoring':{}}");
+ }
+
+ public void testSetLayersOffering0() throws Exception {
+ mRecorder.setLayersOffering(layersOffering0);
+ assertJsonMsgEquals("{'setLayersOffering':{}}");
+ }
+
+ public void testSetLayersOffering2() throws Exception {
+ mRecorder.setLayersOffering(layersOffering2);
+ assertJsonMsgEquals("{'setLayersOffering':{'layerDependency':["
+ + "{'layer':{'type':3,'subtype':5,'version':4}},"
+ + "{'layer':{'type':1,'subtype':3,'version':2},'dependency':["
+ + "{'type':2,'subtype':4,'version':3},{'type':3,'subtype':5,'version':4}]}"
+ + "]}}");
+ }
+
+ public void testGetPublisherId() throws Exception {
+ mRecorder.getPublisherId(9);
+ assertJsonMsgEquals("{'getPublisherId':{'publisherId':9}}");
+ }
+
+ public void testAddSubscription() throws Exception {
+ mRecorder.addSubscription(42, layer1);
+ assertJsonMsgEquals(
+ "{'addSubscription':{'sequenceNumber':42,'layer':{'type':1,'subtype':3,'version':2}}}"
+ );
+ }
+
+ public void testRemoveSubscription() throws Exception {
+ mRecorder.removeSubscription(42, layer1);
+ assertJsonMsgEquals("{'removeSubscription':"
+ + "{'sequenceNumber':42,'layer':{'type':1,'subtype':3,'version':2}}}");
+ }
+
+ public void testAddPromiscuousSubscription() throws Exception {
+ mRecorder.addPromiscuousSubscription(42);
+ assertJsonMsgEquals("{'addPromiscuousSubscription':{'sequenceNumber':42}}");
+ }
+
+ public void testRemovePromiscuousSubscription() throws Exception {
+ mRecorder.removePromiscuousSubscription(42);
+ assertJsonMsgEquals("{'removePromiscuousSubscription':{'sequenceNumber':42}}");
+ }
+
+ public void testAddHalSubscription() throws Exception {
+ mRecorder.addHalSubscription(42, layer1);
+ assertJsonMsgEquals("{'addHalSubscription':"
+ + "{'sequenceNumber':42,'layer':{'type':1,'subtype':3,'version':2}}}");
+ }
+
+ public void testRemoveHalSubscription() throws Exception {
+ mRecorder.removeHalSubscription(42, layer1);
+ assertJsonMsgEquals("{'removeHalSubscription':"
+ + "{'sequenceNumber':42,'layer':{'type':1,'subtype':3,'version':2}}}");
+ }
+
+ public void testSetPublisherLayersOffering() throws Exception {
+ mRecorder.setPublisherLayersOffering(layersOffering1);
+ assertJsonMsgEquals("{'setPublisherLayersOffering':{'layerDependency':["
+ + "{'layer':{'type':3,'subtype':5,'version':4}}]}}");
+ }
+
+ public void testSetHalPublisherLayersOffering() throws Exception {
+ mRecorder.setHalPublisherLayersOffering(layersOffering1);
+ assertJsonMsgEquals("{'setHalPublisherLayersOffering':{'layerDependency':["
+ + "{'layer':{'type':3,'subtype':5,'version':4}}]}}");
+ }
+
+ public void testSubscribeToPublisher() throws Exception {
+ mRecorder.subscribe(layer1, 99);
+ assertJsonMsgEquals(
+ "{'subscribe':{'publisherId':99, 'layer':{'type':1,'subtype':3,'version':2}}}");
+ }
+
+ public void testUnsubscribeToPublisher() throws Exception {
+ mRecorder.unsubscribe(layer1, 99);
+ assertJsonMsgEquals(
+ "{'unsubscribe':{'publisherId':99, 'layer':{'type':1,'subtype':3,'version':2}}}}");
+ }
+
+ private void assertJsonMsgEquals(String expectJson) throws Exception {
+ // Escaping double quotes in a JSON string is really noisy. The test data uses single
+ // quotes instead, which gets replaced here.
+ JSONObject expect = new JSONObject(expectJson.replace("'", "\""));
+ JSONObject got = new JSONObject(mWriter.mMsg);
+ assertTrue(similar(expect, got));
+ }
+
+ /*
+ * Determine if two JSONObjects are similar.
+ * They must contain the same set of names which must be associated with
+ * similar values.
+ */
+ private boolean similar(JSONObject expect, JSONObject got) {
+ try {
+ if (!expect.keySet().equals(got.keySet())) {
+ return false;
+ }
+
+ for (String key : expect.keySet()) {
+ Object valueExpect = expect.get(key);
+ Object valueGot = got.get(key);
+
+ if (valueExpect == valueGot) {
+ continue;
+ }
+
+ if (valueExpect == null) {
+ return false;
+ }
+
+ if (valueExpect instanceof JSONObject) {
+ return similar((JSONObject) valueExpect, (JSONObject) valueGot);
+ } else if (valueExpect instanceof JSONArray) {
+ // Equal JSONArray have the same length and one contains the other.
+ JSONArray expectArray = (JSONArray) valueExpect;
+ JSONArray gotArray = (JSONArray) valueGot;
+
+ if (expectArray.length() != gotArray.length()) {
+ return false;
+ }
+
+ for (int i = 0; i < expectArray.length(); i++) {
+ boolean gotContainsSimilar = false;
+ for (int j = 0; j < gotArray.length(); j++) {
+ if (similar((JSONObject) expectArray.get(i),
+ (JSONObject) gotArray.get(j))) {
+ gotContainsSimilar = true;
+ break;
+ }
+ }
+ if (!gotContainsSimilar) {
+ return false;
+ }
+ }
+ } else if (!valueExpect.equals(valueGot)) {
+ return false;
+ }
+ }
+
+ } catch (JSONException e) {
+ Log.d(TAG, "Could not compare JSONObjects: " + e);
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java b/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java
index d8e344b..0d5c427 100644
--- a/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java
+++ b/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java
@@ -23,13 +23,22 @@
import android.car.vms.VmsPublisherClientService;
import android.car.vms.VmsSubscriptionState;
import android.util.Log;
-import java.util.List;
import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
/**
* This service is launched during the tests in VmsPublisherSubscriberTest. It publishes a property
* that is going to be verified in the test.
*
+ * The service makes offering for pre-defined layers which verifies availability notifications for
+ * subscribers without them actively being subscribed to a layer, and also echos all the
+ * subscription requests with an offering for that layer.
+ * For example, without any subscription request from any client, this service will make offering
+ * to layer X. If a client will subscribe later to layer Y, this service will respond with offering
+ * to both layers X and Y.
+ *
* Note that the subscriber can subscribe before the publisher finishes initialization. To cover
* both potential scenarios, this service publishes the test message in onVmsSubscriptionChange
* and in onVmsPublisherServiceReady. See comments below.
@@ -53,25 +62,33 @@
private void initializeMockPublisher(VmsSubscriptionState subscriptionState) {
Log.d(TAG, "Initializing Mock publisher");
- getPublisherStaticId(VmsPublisherSubscriberTest.PAYLOAD);
+ int publisherId = getPublisherId(VmsPublisherSubscriberTest.PAYLOAD);
publishIfNeeded(subscriptionState);
- declareOffering(subscriptionState);
+ declareOffering(subscriptionState, publisherId);
}
private void publishIfNeeded(VmsSubscriptionState subscriptionState) {
for (VmsLayer layer : subscriptionState.getLayers()) {
if (layer.equals(VmsPublisherSubscriberTest.LAYER)) {
- publish(VmsPublisherSubscriberTest.LAYER, VmsPublisherSubscriberTest.PAYLOAD);
+ publish(VmsPublisherSubscriberTest.LAYER,
+ VmsPublisherSubscriberTest.EXPECTED_PUBLISHER_ID,
+ VmsPublisherSubscriberTest.PAYLOAD);
}
}
}
- private void declareOffering(VmsSubscriptionState subscriptionState) {
- List<VmsLayerDependency> dependencies = new ArrayList<>();
+ private void declareOffering(VmsSubscriptionState subscriptionState, int publisherId) {
+ Set<VmsLayerDependency> dependencies = new HashSet<>();
+
+ // Add all layers from the subscription state.
for( VmsLayer layer : subscriptionState.getLayers()) {
dependencies.add(new VmsLayerDependency(layer));
}
- VmsLayersOffering offering = new VmsLayersOffering(dependencies);
+
+ // Add default test layers.
+ dependencies.add(new VmsLayerDependency(VmsPublisherSubscriberTest.LAYER));
+
+ VmsLayersOffering offering = new VmsLayersOffering(dependencies, publisherId);
setLayersOffering(offering);
}
}
diff --git a/tests/carservice_test/src/com/android/car/test/VmsPublisherClientServiceTest.java b/tests/carservice_test/src/com/android/car/test/VmsPublisherClientServiceTest.java
index c22f63a..053bab7 100644
--- a/tests/carservice_test/src/com/android/car/test/VmsPublisherClientServiceTest.java
+++ b/tests/carservice_test/src/com/android/car/test/VmsPublisherClientServiceTest.java
@@ -27,10 +27,10 @@
import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
-import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
-import android.hardware.automotive.vehicle.V2_1.VmsBaseMessageIntegerValuesIndex;
-import android.hardware.automotive.vehicle.V2_1.VmsSimpleMessageIntegerValuesIndex;
-import android.hardware.automotive.vehicle.V2_1.VmsMessageType;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_0.VmsBaseMessageIntegerValuesIndex;
+import android.hardware.automotive.vehicle.V2_0.VmsMessageWithLayerIntegerValuesIndex;
+import android.hardware.automotive.vehicle.V2_0.VmsMessageType;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
@@ -50,8 +50,12 @@
private static final String TAG = "VmsPublisherTest";
private static final int MOCK_PUBLISHER_LAYER_ID = 12;
private static final int MOCK_PUBLISHER_LAYER_VERSION = 34;
- public static final VmsLayer MOCK_PUBLISHER_LAYER = new VmsLayer(MOCK_PUBLISHER_LAYER_ID,
- MOCK_PUBLISHER_LAYER_VERSION);
+ private static final int MOCK_PUBLISHER_LAYER_SUBTYPE = 56;
+ public static final int MOCK_PUBLISHER_ID = 1234;
+ public static final VmsLayer MOCK_PUBLISHER_LAYER =
+ new VmsLayer(MOCK_PUBLISHER_LAYER_ID,
+ MOCK_PUBLISHER_LAYER_SUBTYPE,
+ MOCK_PUBLISHER_LAYER_VERSION);
public static final byte[] PAYLOAD = new byte[]{1, 1, 2, 3, 5, 8, 13};
private HalHandler mHalHandler;
@@ -97,10 +101,11 @@
private VehiclePropValue getHalSubscriptionRequest() {
return VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
- .addIntValue(VmsMessageType.SUBSCRIBE)
- .addIntValue(MOCK_PUBLISHER_LAYER_ID)
- .addIntValue(MOCK_PUBLISHER_LAYER_VERSION)
- .build();
+ .addIntValue(VmsMessageType.SUBSCRIBE)
+ .addIntValue(MOCK_PUBLISHER_LAYER_ID)
+ .addIntValue(MOCK_PUBLISHER_LAYER_SUBTYPE)
+ .addIntValue(MOCK_PUBLISHER_LAYER_VERSION)
+ .build();
}
@Override
@@ -140,9 +145,9 @@
// the semaphore will not be released.
assertTrue(mHalHandlerSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
VehiclePropValue.RawValue rawValue = mHalHandler.getValue().value;
- int messageType = rawValue.int32Values.get(VmsSimpleMessageIntegerValuesIndex.VMS_MESSAGE_TYPE);
- int layerId = rawValue.int32Values.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_ID);
- int layerVersion = rawValue.int32Values.get(VmsSimpleMessageIntegerValuesIndex.VMS_LAYER_VERSION);
+ int messageType = rawValue.int32Values.get(VmsMessageWithLayerIntegerValuesIndex.MESSAGE_TYPE);
+ int layerId = rawValue.int32Values.get(VmsMessageWithLayerIntegerValuesIndex.LAYER_TYPE);
+ int layerVersion = rawValue.int32Values.get(VmsMessageWithLayerIntegerValuesIndex.LAYER_VERSION);
byte[] payload = new byte[rawValue.bytes.size()];
for (int i = 0; i < rawValue.bytes.size(); ++i) {
payload[i] = rawValue.bytes.get(i);
@@ -162,7 +167,7 @@
// If this is the data message release the semaphone so the test can continue.
ArrayList<Integer> int32Values = value.value.int32Values;
- if (int32Values.get(VmsBaseMessageIntegerValuesIndex.VMS_MESSAGE_TYPE) ==
+ if (int32Values.get(VmsBaseMessageIntegerValuesIndex.MESSAGE_TYPE) ==
VmsMessageType.DATA) {
mHalHandlerSemaphore.release();
}
@@ -187,4 +192,4 @@
return mValue;
}
}
-}
+}
\ No newline at end of file
diff --git a/tests/carservice_test/src/com/android/car/test/VmsPublisherPermissionsTest.java b/tests/carservice_test/src/com/android/car/test/VmsPublisherPermissionsTest.java
index 739f5d0..1d14c57 100644
--- a/tests/carservice_test/src/com/android/car/test/VmsPublisherPermissionsTest.java
+++ b/tests/carservice_test/src/com/android/car/test/VmsPublisherPermissionsTest.java
@@ -26,9 +26,10 @@
import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
-import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
-import android.hardware.automotive.vehicle.V2_1.VmsBaseMessageIntegerValuesIndex;
-import android.hardware.automotive.vehicle.V2_1.VmsMessageType;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_0.VmsBaseMessageIntegerValuesIndex;
+import android.hardware.automotive.vehicle.V2_0.VmsMessageType;
+import android.test.suitebuilder.annotation.MediumTest;
import com.android.car.R;
import com.android.car.vehiclehal.VehiclePropValueBuilder;
@@ -40,10 +41,12 @@
import java.util.concurrent.TimeUnit;
@FutureFeature
+@MediumTest
public class VmsPublisherPermissionsTest extends MockedCarTestBase {
private static final String TAG = "VmsPublisherTest";
private static final int MOCK_PUBLISHER_LAYER_ID = 0;
private static final int MOCK_PUBLISHER_LAYER_VERSION = 0;
+ private static final int MOCK_PUBLISHER_LAYER_FUSION_INT_VALUE = 0;
private HalHandler mHalHandler;
// Used to block until the HAL property is updated in HalHandler.onPropertySet.
@@ -95,6 +98,7 @@
.addIntValue(VmsMessageType.SUBSCRIBE)
.addIntValue(MOCK_PUBLISHER_LAYER_ID)
.addIntValue(MOCK_PUBLISHER_LAYER_VERSION)
+ .addIntValue(MOCK_PUBLISHER_LAYER_FUSION_INT_VALUE)
.build();
}
@@ -147,7 +151,7 @@
public synchronized void onPropertySet(VehiclePropValue value) {
// If this is the data message release the semaphore so the test can continue.
ArrayList<Integer> int32Values = value.value.int32Values;
- if (int32Values.get(VmsBaseMessageIntegerValuesIndex.VMS_MESSAGE_TYPE) ==
+ if (int32Values.get(VmsBaseMessageIntegerValuesIndex.MESSAGE_TYPE) ==
VmsMessageType.DATA) {
mHalHandlerSemaphore.release();
}
diff --git a/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java b/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java
index 3b3a94f..5b387d2 100644
--- a/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java
+++ b/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java
@@ -20,6 +20,7 @@
import android.car.Car;
import android.car.VehicleAreaType;
import android.car.annotation.FutureFeature;
+import android.car.vms.VmsAssociatedLayer;
import android.car.vms.VmsLayer;
import android.car.vms.VmsSubscriberManager;
import android.content.Context;
@@ -28,29 +29,48 @@
import android.content.res.Resources;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
-import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.test.suitebuilder.annotation.MediumTest;
+
import com.android.car.vehiclehal.test.MockedVehicleHal;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
@FutureFeature
+@MediumTest
public class VmsPublisherSubscriberTest extends MockedCarTestBase {
private static final int LAYER_ID = 88;
private static final int LAYER_VERSION = 19;
- private static final int EXPECTED_PUBLISHER_ID = 0;
+ private static final int LAYER_SUBTYPE = 55;
private static final String TAG = "VmsPubSubTest";
- public static final VmsLayer LAYER = new VmsLayer(LAYER_ID, LAYER_VERSION);
+ // The expected publisher ID is 0 since it the expected assigned ID from the VMS core.
+ public static final int EXPECTED_PUBLISHER_ID = 0;
+ public static final VmsLayer LAYER = new VmsLayer(LAYER_ID, LAYER_SUBTYPE, LAYER_VERSION);
+ public static final VmsAssociatedLayer ASSOCIATED_LAYER =
+ new VmsAssociatedLayer(LAYER, new HashSet<>(Arrays.asList(EXPECTED_PUBLISHER_ID)));
public static final byte[] PAYLOAD = new byte[]{2, 3, 5, 7, 11, 13, 17};
- private static final List<VmsLayer> AVAILABLE_LAYERS = new ArrayList<>(Arrays.asList(LAYER));
+
+ private static final List<VmsAssociatedLayer> AVAILABLE_ASSOCIATED_LAYERS =
+ new ArrayList<>(Arrays.asList(ASSOCIATED_LAYER));
+
+
+ private static final int SUBSCRIBED_LAYER_ID = 89;
+ public static final VmsLayer SUBSCRIBED_LAYER =
+ new VmsLayer(SUBSCRIBED_LAYER_ID, LAYER_SUBTYPE, LAYER_VERSION);
+ public static final VmsAssociatedLayer ASSOCIATED_SUBSCRIBED_LAYER =
+ new VmsAssociatedLayer(SUBSCRIBED_LAYER, new HashSet<>(Arrays.asList(EXPECTED_PUBLISHER_ID)));
+ private static final List<VmsAssociatedLayer> AVAILABLE_ASSOCIATED_LAYERS_WITH_SUBSCRIBED_LAYER =
+ new ArrayList<>(Arrays.asList(ASSOCIATED_LAYER, ASSOCIATED_SUBSCRIBED_LAYER));
+
private HalHandler mHalHandler;
- // Used to block until a value is propagated to the TestListener.onVmsMessageReceived.
+ // Used to block until a value is propagated to the TestClientCallback.onVmsMessageReceived.
private Semaphore mSubscriberSemaphore;
private Semaphore mAvailabilitySemaphore;
@@ -117,13 +137,13 @@
if (!VmsTestUtils.canRunTest(TAG)) return;
VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
Car.VMS_SUBSCRIBER_SERVICE);
- TestListener listener = new TestListener();
- vmsSubscriberManager.setListener(listener);
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
vmsSubscriberManager.subscribe(LAYER);
assertTrue(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
- assertEquals(LAYER, listener.getLayer());
- assertTrue(Arrays.equals(PAYLOAD, listener.getPayload()));
+ assertEquals(LAYER, clientCallback.getLayer());
+ assertTrue(Arrays.equals(PAYLOAD, clientCallback.getPayload()));
}
/**
@@ -137,8 +157,8 @@
VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
Car.VMS_SUBSCRIBER_SERVICE);
// Subscribe to layer as a way to make sure the mock client completed setting the information.
- TestListener listener = new TestListener();
- vmsSubscriberManager.setListener(listener);
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
vmsSubscriberManager.subscribe(LAYER);
assertTrue(mAvailabilitySemaphore.tryAcquire(2L, TimeUnit.SECONDS));
@@ -148,26 +168,42 @@
}
/**
- * The Mock service offers all the subscribed layers as available layers, so in this
- * test the listener subscribes to a layer and verifies that it gets the notification that it
- * is available.
+ * The Mock service offers all the subscribed layers as available layers.
+ * In this test the client subscribes to a layer and verifies that it gets the
+ * notification that it is available.
*/
- public void testAvailability() throws Exception {
+ public void testAvailabilityWithSubscription() throws Exception {
if (!VmsTestUtils.canRunTest(TAG)) return;
VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
Car.VMS_SUBSCRIBER_SERVICE);
- TestListener listener = new TestListener();
- vmsSubscriberManager.setListener(listener);
- vmsSubscriberManager.subscribe(LAYER);
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
+ vmsSubscriberManager.subscribe(SUBSCRIBED_LAYER);
assertTrue(mAvailabilitySemaphore.tryAcquire(2L, TimeUnit.SECONDS));
- assertEquals(AVAILABLE_LAYERS, listener.getAvailalbeLayers());
+ assertEquals(AVAILABLE_ASSOCIATED_LAYERS_WITH_SUBSCRIBED_LAYER, clientCallback.getAvailalbeLayers());
+ }
+
+ /**
+ * The Mock service offers all the subscribed layers as available layers, so in this
+ * test the client subscribes to a layer and verifies that it gets the notification that it
+ * is available.
+ */
+ public void testAvailabilityWithoutSubscription() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
+
+ assertTrue(mAvailabilitySemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ assertEquals(AVAILABLE_ASSOCIATED_LAYERS, clientCallback.getAvailalbeLayers());
}
private class HalHandler implements MockedVehicleHal.VehicleHalPropertyHandler {
}
- private class TestListener implements VmsSubscriberManager.VmsSubscriberClientListener {
+ private class TestClientCallback implements VmsSubscriberManager.VmsSubscriberClientCallback {
private VmsLayer mLayer;
private byte[] mPayload;
private List<VmsLayer> mAvailableLayers;
@@ -182,17 +218,11 @@
}
@Override
- public void onLayersAvailabilityChange(List<VmsLayer> availableLayers) {
- assertEquals(AVAILABLE_LAYERS, availableLayers);
+ public void onLayersAvailabilityChanged(List<VmsLayer> availableLayers) {
mAvailableLayers = availableLayers;
mAvailabilitySemaphore.release();
}
- @Override
- public void onCarDisconnected() {
-
- }
-
public VmsLayer getLayer() {
return mLayer;
}
diff --git a/tests/carservice_test/src/com/android/car/test/VmsSubscriberManagerTest.java b/tests/carservice_test/src/com/android/car/test/VmsSubscriberManagerTest.java
index 063d5cf..9e9a5d0 100644
--- a/tests/carservice_test/src/com/android/car/test/VmsSubscriberManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/test/VmsSubscriberManagerTest.java
@@ -16,25 +16,31 @@
package com.android.car.test;
+import static org.junit.Assume.assumeTrue;
+
import android.car.Car;
import android.car.VehicleAreaType;
import android.car.annotation.FutureFeature;
+import android.car.vms.VmsAssociatedLayer;
import android.car.vms.VmsLayer;
import android.car.vms.VmsSubscriberManager;
-import android.car.vms.VmsSubscriberManager.VmsSubscriberClientListener;
+import android.car.vms.VmsSubscriberManager.VmsSubscriberClientCallback;
import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
-import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
-import android.hardware.automotive.vehicle.V2_1.VmsMessageType;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_0.VmsMessageType;
import android.os.SystemClock;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
import com.android.car.vehiclehal.VehiclePropValueBuilder;
import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
+
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
@@ -42,20 +48,38 @@
@MediumTest
public class VmsSubscriberManagerTest extends MockedCarTestBase {
private static final String TAG = "VmsSubscriberManagerTest";
+ private static final int PUBLISHER_ID = 17;
+ private static final int WRONG_PUBLISHER_ID = 26;
+ private static final Set<Integer> PUBLISHERS_LIST = new HashSet<Integer>(Arrays.asList(PUBLISHER_ID));
+
private static final int SUBSCRIPTION_LAYER_ID = 2;
private static final int SUBSCRIPTION_LAYER_VERSION = 3;
+ private static final int MOCK_PUBLISHER_LAYER_SUBTYPE = 444;
private static final VmsLayer SUBSCRIPTION_LAYER = new VmsLayer(SUBSCRIPTION_LAYER_ID,
+ MOCK_PUBLISHER_LAYER_SUBTYPE,
SUBSCRIPTION_LAYER_VERSION);
+ private static final VmsAssociatedLayer SUBSCRIPTION_ASSOCIATED_LAYER =
+ new VmsAssociatedLayer(SUBSCRIPTION_LAYER, PUBLISHERS_LIST);
private static final int SUBSCRIPTION_DEPENDANT_LAYER_ID_1 = 4;
private static final int SUBSCRIPTION_DEPENDANT_LAYER_VERSION_1 = 5;
private static final VmsLayer SUBSCRIPTION_DEPENDANT_LAYER_1 =
- new VmsLayer(SUBSCRIPTION_DEPENDANT_LAYER_ID_1, SUBSCRIPTION_DEPENDANT_LAYER_VERSION_1);
+ new VmsLayer(SUBSCRIPTION_DEPENDANT_LAYER_ID_1,
+ MOCK_PUBLISHER_LAYER_SUBTYPE,
+ SUBSCRIPTION_DEPENDANT_LAYER_VERSION_1);
+
+ private static final VmsAssociatedLayer SUBSCRIPTION_DEPENDANT_ASSOCIATED_LAYER_1 =
+ new VmsAssociatedLayer(SUBSCRIPTION_DEPENDANT_LAYER_1, PUBLISHERS_LIST);
private static final int SUBSCRIPTION_DEPENDANT_LAYER_ID_2 = 6;
private static final int SUBSCRIPTION_DEPENDANT_LAYER_VERSION_2 = 7;
private static final VmsLayer SUBSCRIPTION_DEPENDANT_LAYER_2 =
- new VmsLayer(SUBSCRIPTION_DEPENDANT_LAYER_ID_2, SUBSCRIPTION_DEPENDANT_LAYER_VERSION_2);
+ new VmsLayer(SUBSCRIPTION_DEPENDANT_LAYER_ID_2,
+ MOCK_PUBLISHER_LAYER_SUBTYPE,
+ SUBSCRIPTION_DEPENDANT_LAYER_VERSION_2);
+
+ private static final VmsAssociatedLayer SUBSCRIPTION_DEPENDANT_ASSOCIATED_LAYER_2 =
+ new VmsAssociatedLayer(SUBSCRIPTION_DEPENDANT_LAYER_2, PUBLISHERS_LIST);
private static final int SUBSCRIPTION_UNSUPPORTED_LAYER_ID = 100;
private static final int SUBSCRIPTION_UNSUPPORTED_LAYER_VERSION = 200;
@@ -64,7 +88,7 @@
private HalHandler mHalHandler;
// Used to block until the HAL property is updated in HalHandler.onPropertySet.
private Semaphore mHalHandlerSemaphore;
- // Used to block until a value is propagated to the TestListener.onVmsMessageReceived.
+ // Used to block until a value is propagated to the TestClientCallback.onVmsMessageReceived.
private Semaphore mSubscriberSemaphore;
@Override
@@ -95,27 +119,171 @@
if (!VmsTestUtils.canRunTest(TAG)) return;
VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
Car.VMS_SUBSCRIBER_SERVICE);
- TestListener listener = new TestListener();
- vmsSubscriberManager.setListener(listener);
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
vmsSubscriberManager.subscribe(SUBSCRIPTION_LAYER);
- // Inject a value and wait for its callback in TestListener.onVmsMessageReceived.
+ // Inject a value and wait for its callback in TestClientCallback.onVmsMessageReceived.
VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
.setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
.setTimestamp(SystemClock.elapsedRealtimeNanos())
.build();
v.value.int32Values.add(VmsMessageType.DATA); // MessageType
v.value.int32Values.add(SUBSCRIPTION_LAYER_ID);
+ v.value.int32Values.add(MOCK_PUBLISHER_LAYER_SUBTYPE);
v.value.int32Values.add(SUBSCRIPTION_LAYER_VERSION);
+ v.value.int32Values.add(PUBLISHER_ID);
v.value.bytes.add((byte) 0xa);
v.value.bytes.add((byte) 0xb);
assertEquals(0, mSubscriberSemaphore.availablePermits());
getMockedVehicleHal().injectEvent(v);
assertTrue(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
- assertEquals(SUBSCRIPTION_LAYER, listener.getLayer());
+ assertEquals(SUBSCRIPTION_LAYER, clientCallback.getLayer());
byte[] expectedPayload = {(byte) 0xa, (byte) 0xb};
- assertTrue(Arrays.equals(expectedPayload, listener.getPayload()));
+ assertTrue(Arrays.equals(expectedPayload, clientCallback.getPayload()));
+ }
+
+
+ // Test injecting a value in the HAL and verifying it propagates to a subscriber.
+ public void testSubscribeToPublisher() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
+ vmsSubscriberManager.subscribe(SUBSCRIPTION_LAYER, PUBLISHER_ID);
+
+ // Inject a value and wait for its callback in TestClientCallback.onVmsMessageReceived.
+ VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
+ .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .build();
+ v.value.int32Values.add(VmsMessageType.DATA); // MessageType
+ v.value.int32Values.add(SUBSCRIPTION_LAYER_ID);
+ v.value.int32Values.add(MOCK_PUBLISHER_LAYER_SUBTYPE);
+ v.value.int32Values.add(SUBSCRIPTION_LAYER_VERSION);
+ v.value.int32Values.add(WRONG_PUBLISHER_ID);
+ v.value.bytes.add((byte) 0xa);
+ v.value.bytes.add((byte) 0xb);
+ assertEquals(0, mSubscriberSemaphore.availablePermits());
+
+ getMockedVehicleHal().injectEvent(v);
+
+ assertFalse(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ }
+
+ // Test injecting a value in the HAL and verifying it propagates to a subscriber.
+ public void testSubscribeFromPublisher() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
+ vmsSubscriberManager.subscribe(SUBSCRIPTION_LAYER, PUBLISHER_ID);
+
+ // Inject a value and wait for its callback in TestClientCallback.onVmsMessageReceived.
+ VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
+ .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .build();
+ v.value.int32Values.add(VmsMessageType.DATA); // MessageType
+ v.value.int32Values.add(SUBSCRIPTION_LAYER_ID);
+ v.value.int32Values.add(MOCK_PUBLISHER_LAYER_SUBTYPE); //<-
+ v.value.int32Values.add(SUBSCRIPTION_LAYER_VERSION);
+ v.value.int32Values.add(PUBLISHER_ID);
+ v.value.bytes.add((byte) 0xa);
+ v.value.bytes.add((byte) 0xb);
+ assertEquals(0, mSubscriberSemaphore.availablePermits());
+
+ getMockedVehicleHal().injectEvent(v);
+ assertTrue(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ assertEquals(SUBSCRIPTION_LAYER, clientCallback.getLayer());
+ byte[] expectedPayload = {(byte) 0xa, (byte) 0xb};
+ assertTrue(Arrays.equals(expectedPayload, clientCallback.getPayload()));
+ }
+
+ // Test injecting a value in the HAL and verifying it does not propagate to a subscriber.
+ public void testUnsubscribe() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
+ vmsSubscriberManager.subscribe(SUBSCRIPTION_LAYER);
+ vmsSubscriberManager.unsubscribe(SUBSCRIPTION_LAYER);
+
+ // Inject a value and wait for its callback in TestClientCallback.onVmsMessageReceived.
+ VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
+ .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .build();
+ v.value.int32Values.add(VmsMessageType.DATA); // MessageType
+ v.value.int32Values.add(SUBSCRIPTION_LAYER_ID);
+ v.value.int32Values.add(MOCK_PUBLISHER_LAYER_SUBTYPE);
+ v.value.int32Values.add(SUBSCRIPTION_LAYER_VERSION);
+ v.value.int32Values.add(PUBLISHER_ID);
+ v.value.bytes.add((byte) 0xa);
+ v.value.bytes.add((byte) 0xb);
+ assertEquals(0, mSubscriberSemaphore.availablePermits());
+
+ getMockedVehicleHal().injectEvent(v);
+ assertFalse(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ }
+
+ // Test injecting a value in the HAL and verifying it does not propagate to a subscriber.
+ public void testSubscribeFromWrongPublisher() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
+ vmsSubscriberManager.subscribe(SUBSCRIPTION_LAYER, PUBLISHER_ID);
+
+ // Inject a value and wait for its callback in TestClientCallback.onVmsMessageReceived.
+ VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
+ .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .build();
+ v.value.int32Values.add(VmsMessageType.DATA); // MessageType
+ v.value.int32Values.add(SUBSCRIPTION_LAYER_ID);
+ v.value.int32Values.add(MOCK_PUBLISHER_LAYER_SUBTYPE);
+ v.value.int32Values.add(SUBSCRIPTION_LAYER_VERSION);
+ v.value.int32Values.add(WRONG_PUBLISHER_ID);
+ v.value.bytes.add((byte) 0xa);
+ v.value.bytes.add((byte) 0xb);
+ assertEquals(0, mSubscriberSemaphore.availablePermits());
+
+ getMockedVehicleHal().injectEvent(v);
+ assertFalse(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ }
+
+ // Test injecting a value in the HAL and verifying it does not propagate to a subscriber.
+ public void testUnsubscribeFromPublisher() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
+ vmsSubscriberManager.subscribe(SUBSCRIPTION_LAYER, PUBLISHER_ID);
+ vmsSubscriberManager.unsubscribe(SUBSCRIPTION_LAYER, PUBLISHER_ID);
+
+ // Inject a value and wait for its callback in TestClientCallback.onVmsMessageReceived.
+ VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
+ .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .build();
+ v.value.int32Values.add(VmsMessageType.DATA); // MessageType
+ v.value.int32Values.add(SUBSCRIPTION_LAYER_ID);
+ v.value.int32Values.add(MOCK_PUBLISHER_LAYER_SUBTYPE);
+ v.value.int32Values.add(SUBSCRIPTION_LAYER_VERSION);
+ v.value.int32Values.add(PUBLISHER_ID);
+ v.value.bytes.add((byte) 0xa);
+ v.value.bytes.add((byte) 0xb);
+ assertEquals(0, mSubscriberSemaphore.availablePermits());
+
+ getMockedVehicleHal().injectEvent(v);
+ assertFalse(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
}
@@ -123,142 +291,263 @@
public void testSubscribeAll() throws Exception {
if (!VmsTestUtils.canRunTest(TAG)) return;
VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
- Car.VMS_SUBSCRIBER_SERVICE);
- TestListener listener = new TestListener();
- vmsSubscriberManager.setListener(listener);
- vmsSubscriberManager.subscribeAll();
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
+ vmsSubscriberManager.startMonitoring();
- // Inject a value and wait for its callback in TestListener.onVmsMessageReceived.
+ // Inject a value and wait for its callback in TestClientCallback.onVmsMessageReceived.
VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
- .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
- .setTimestamp(SystemClock.elapsedRealtimeNanos())
- .build();
+ .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .build();
v.value.int32Values.add(VmsMessageType.DATA); // MessageType
v.value.int32Values.add(SUBSCRIPTION_LAYER_ID);
+ v.value.int32Values.add(MOCK_PUBLISHER_LAYER_SUBTYPE);
v.value.int32Values.add(SUBSCRIPTION_LAYER_VERSION);
+ v.value.int32Values.add(PUBLISHER_ID);
v.value.bytes.add((byte) 0xa);
v.value.bytes.add((byte) 0xb);
assertEquals(0, mSubscriberSemaphore.availablePermits());
getMockedVehicleHal().injectEvent(v);
assertTrue(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
- assertEquals(SUBSCRIPTION_LAYER, listener.getLayer());
+ assertEquals(SUBSCRIPTION_LAYER, clientCallback.getLayer());
byte[] expectedPayload = {(byte) 0xa, (byte) 0xb};
- assertTrue(Arrays.equals(expectedPayload, listener.getPayload()));
+ assertTrue(Arrays.equals(expectedPayload, clientCallback.getPayload()));
}
-
// Test injecting a value in the HAL and verifying it propagates to a subscriber.
public void testSimpleAvailableLayers() throws Exception {
if (!VmsTestUtils.canRunTest(TAG)) return;
VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
- Car.VMS_SUBSCRIBER_SERVICE);
- TestListener listener = new TestListener();
- vmsSubscriberManager.setListener(listener);
- vmsSubscriberManager.subscribe(SUBSCRIPTION_LAYER);
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
- // Inject a value and wait for its callback in TestListener.onLayersAvailabilityChange.
+ // Inject a value and wait for its callback in TestClientCallback.onLayersAvailabilityChanged.
VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
- .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
- .setTimestamp(SystemClock.elapsedRealtimeNanos())
- .build();
+ .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .build();
/*
Offering:
- Layer | Dependency
- ====================
- (2, 3) | {}
+ Layer | Dependency
+ ===============================
+ (2, 3, 444), [17] | {}
Expected availability:
- {(2, 3)}
+ {(2, 3, 444 [17])}
*/
v.value.int32Values.addAll(
- Arrays.asList(
- VmsMessageType.OFFERING, // MessageType
- 1, // Number of offered layers
+ Arrays.asList(
+ VmsMessageType.OFFERING, // MessageType
+ PUBLISHER_ID,
+ 1, // Number of offered layers
- SUBSCRIPTION_LAYER_ID,
- SUBSCRIPTION_LAYER_VERSION,
- 0 // number of dependencies for layer
- )
+ SUBSCRIPTION_LAYER_ID,
+ MOCK_PUBLISHER_LAYER_SUBTYPE,
+ SUBSCRIPTION_LAYER_VERSION,
+ 0 // number of dependencies for layer
+ )
);
assertEquals(0, mSubscriberSemaphore.availablePermits());
- List<VmsLayer> expectedAvailableLayers = new ArrayList<>(Arrays.asList(SUBSCRIPTION_LAYER));
+ getMockedVehicleHal().injectEvent(v);
+ assertTrue(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ List<VmsAssociatedLayer> expectedAvailableLayers =
+ new ArrayList<>(Arrays.asList(SUBSCRIPTION_ASSOCIATED_LAYER));
+ assertTrue(expectedAvailableLayers.containsAll(clientCallback.getAvailableLayers()));
+ assertEquals(expectedAvailableLayers.size(), clientCallback.getAvailableLayers().size());
+ }
+
+ // Test injecting a value in the HAL and verifying it propagates to a subscriber after it has
+ // subscribed to a layer.
+ public void testSimpleAvailableLayersAfterSubscription() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
+ vmsSubscriberManager.subscribe(SUBSCRIPTION_LAYER);
+
+ // Inject a value and wait for its callback in TestClientCallback.onLayersAvailabilityChanged.
+ VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
+ .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .build();
+ /*
+ Offering:
+ Layer | Dependency
+ ===============================
+ (2, 3, 444), [17] | {}
+
+ Expected availability:
+ {(2, 3, 444 [17])}
+ */
+ v.value.int32Values.addAll(
+ Arrays.asList(
+ VmsMessageType.OFFERING, // MessageType
+ PUBLISHER_ID,
+ 1, // Number of offered layers
+
+ SUBSCRIPTION_LAYER_ID,
+ MOCK_PUBLISHER_LAYER_SUBTYPE,
+ SUBSCRIPTION_LAYER_VERSION,
+ 0 // number of dependencies for layer
+ )
+ );
+
+ assertEquals(0, mSubscriberSemaphore.availablePermits());
getMockedVehicleHal().injectEvent(v);
assertTrue(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
- assertEquals(expectedAvailableLayers, listener.getAvailableLayers());
+ List<VmsAssociatedLayer> expectedAvailableLayers =
+ new ArrayList<>(Arrays.asList(SUBSCRIPTION_ASSOCIATED_LAYER));
+ assertTrue(expectedAvailableLayers.containsAll(clientCallback.getAvailableLayers()));
+ assertEquals(expectedAvailableLayers.size(), clientCallback.getAvailableLayers().size());
}
+ // Test injecting a value in the HAL and verifying it does not propagates to a subscriber after
+ // it has unregistered its callback.
+ public void testSimpleAvailableLayersAfterUnregister() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
+ vmsSubscriberManager.unregisterClientCallback();
+
+
+ // Inject a value and wait for its callback in TestClientCallback.onLayersAvailabilityChanged.
+ VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
+ .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .build();
+ /*
+ Offering:
+ Layer | Dependency
+ ===============================
+ (2, 3, 444), [17] | {}
+
+ Expected availability:
+ {(2, 3, 444 [17])}
+ */
+ v.value.int32Values.addAll(
+ Arrays.asList(
+ VmsMessageType.OFFERING, // MessageType
+ PUBLISHER_ID,
+ 1, // Number of offered layers
+
+ SUBSCRIPTION_LAYER_ID,
+ SUBSCRIPTION_LAYER_VERSION,
+ MOCK_PUBLISHER_LAYER_SUBTYPE,
+ 0 // number of dependencies for layer
+ )
+ );
+
+ assertEquals(0, mSubscriberSemaphore.availablePermits());
+ getMockedVehicleHal().injectEvent(v);
+ assertFalse(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ }
+
+ // Test injecting a value in the HAL and verifying it does not propagates to a subscriber after
+ // it has unregistered its callback.
+ public void testSomething() throws Exception {
+ if (!VmsTestUtils.canRunTest(TAG)) return;
+ VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
+ vmsSubscriberManager.subscribe(SUBSCRIPTION_LAYER);
+ try {
+ vmsSubscriberManager.unregisterClientCallback();
+ } catch (IllegalArgumentException e) {
+ return;
+ }
+ fail();
+ }
+
+
// Test injecting a value in the HAL and verifying it propagates to a subscriber.
public void testComplexAvailableLayers() throws Exception {
if (!VmsTestUtils.canRunTest(TAG)) return;
VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
- Car.VMS_SUBSCRIBER_SERVICE);
- TestListener listener = new TestListener();
- vmsSubscriberManager.setListener(listener);
- vmsSubscriberManager.subscribe(SUBSCRIPTION_LAYER);
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
- // Inject a value and wait for its callback in TestListener.onLayersAvailabilityChange.
+ // Inject a value and wait for its callback in TestClientCallback.onLayersAvailabilityChanged.
VehiclePropValue v = VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
- .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
- .setTimestamp(SystemClock.elapsedRealtimeNanos())
- .build();
+ .setAreaId(VehicleAreaType.VEHICLE_AREA_TYPE_NONE)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .build();
/*
Offering:
Layer | Dependency
====================
- (2, 3) | {}
- (4, 5) | {(2, 3)}
- (6, 7) | {(2, 3), (4, 5)}
- (6, 7) | {(100, 200)}
+ (2, 3, 444), [17] | {}
+ (4, 5, 444), [17] | {(2, 3)}
+ (6, 7, 444), [17] | {(2, 3), (4, 5)}
+ (6, 7, 444), [17] | {(100, 200)}
Expected availability:
- {(2, 3), (4, 5), (6, 7)}
+ {(2, 3, 444 [17]), (4, 5, 444 [17]), (6, 7, 444 [17])}
*/
v.value.int32Values.addAll(
- Arrays.asList(
- VmsMessageType.OFFERING, // MessageType
- 4, // Number of offered layers
+ Arrays.asList(
+ VmsMessageType.OFFERING, // MessageType
+ PUBLISHER_ID,
+ 4, // Number of offered layers
- SUBSCRIPTION_LAYER_ID,
- SUBSCRIPTION_LAYER_VERSION,
- 0, // number of dependencies for layer
+ SUBSCRIPTION_LAYER_ID,
+ MOCK_PUBLISHER_LAYER_SUBTYPE,
+ SUBSCRIPTION_LAYER_VERSION,
+ 0, // number of dependencies for layer
- SUBSCRIPTION_DEPENDANT_LAYER_ID_1,
- SUBSCRIPTION_DEPENDANT_LAYER_VERSION_1,
- 1, // number of dependencies for layer
- SUBSCRIPTION_LAYER_ID,
- SUBSCRIPTION_LAYER_VERSION,
+ SUBSCRIPTION_DEPENDANT_LAYER_ID_1,
+ MOCK_PUBLISHER_LAYER_SUBTYPE,
+ SUBSCRIPTION_DEPENDANT_LAYER_VERSION_1,
+ 1, // number of dependencies for layer
+ SUBSCRIPTION_LAYER_ID,
+ MOCK_PUBLISHER_LAYER_SUBTYPE,
+ SUBSCRIPTION_LAYER_VERSION,
- SUBSCRIPTION_DEPENDANT_LAYER_ID_2,
- SUBSCRIPTION_DEPENDANT_LAYER_VERSION_2,
- 2, // number of dependencies for layer
- SUBSCRIPTION_LAYER_ID,
- SUBSCRIPTION_LAYER_VERSION,
- SUBSCRIPTION_DEPENDANT_LAYER_ID_1,
- SUBSCRIPTION_DEPENDANT_LAYER_VERSION_1,
+ SUBSCRIPTION_DEPENDANT_LAYER_ID_2,
+ MOCK_PUBLISHER_LAYER_SUBTYPE,
+ SUBSCRIPTION_DEPENDANT_LAYER_VERSION_2,
+ 2, // number of dependencies for layer
+ SUBSCRIPTION_LAYER_ID,
+ MOCK_PUBLISHER_LAYER_SUBTYPE,
+ SUBSCRIPTION_LAYER_VERSION,
+ SUBSCRIPTION_DEPENDANT_LAYER_ID_1,
+ MOCK_PUBLISHER_LAYER_SUBTYPE,
+ SUBSCRIPTION_DEPENDANT_LAYER_VERSION_1,
- SUBSCRIPTION_DEPENDANT_LAYER_ID_2,
- SUBSCRIPTION_DEPENDANT_LAYER_VERSION_2,
- 1, // number of dependencies for layer
- SUBSCRIPTION_UNSUPPORTED_LAYER_ID,
- SUBSCRIPTION_UNSUPPORTED_LAYER_VERSION
- )
+ SUBSCRIPTION_DEPENDANT_LAYER_ID_2,
+ MOCK_PUBLISHER_LAYER_SUBTYPE,
+ SUBSCRIPTION_DEPENDANT_LAYER_VERSION_2,
+ 1, // number of dependencies for layer
+ SUBSCRIPTION_UNSUPPORTED_LAYER_ID,
+ MOCK_PUBLISHER_LAYER_SUBTYPE,
+ SUBSCRIPTION_UNSUPPORTED_LAYER_VERSION
+ )
);
assertEquals(0, mSubscriberSemaphore.availablePermits());
- List<VmsLayer> expectedAvailableLayers =
- new ArrayList<>(Arrays.asList(SUBSCRIPTION_LAYER,
- SUBSCRIPTION_DEPENDANT_LAYER_1,
- SUBSCRIPTION_DEPENDANT_LAYER_2));
-
+ List<VmsAssociatedLayer> expectedAvailableLayers =
+ new ArrayList<>(Arrays.asList(
+ SUBSCRIPTION_ASSOCIATED_LAYER,
+ SUBSCRIPTION_DEPENDANT_ASSOCIATED_LAYER_1,
+ SUBSCRIPTION_DEPENDANT_ASSOCIATED_LAYER_2
+ ));
getMockedVehicleHal().injectEvent(v);
assertTrue(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
- assertEquals(expectedAvailableLayers, listener.getAvailableLayers());
+ assertTrue(expectedAvailableLayers.containsAll(clientCallback.getAvailableLayers()));
+ assertEquals(expectedAvailableLayers.size(), clientCallback.getAvailableLayers().size());
}
private class HalHandler implements VehicleHalPropertyHandler {
@@ -290,8 +579,7 @@
}
}
-
- private class TestListener implements VmsSubscriberClientListener{
+ private class TestClientCallback implements VmsSubscriberClientCallback {
private VmsLayer mLayer;
private byte[] mPayload;
private List<VmsLayer> mAvailableLayers = new ArrayList<>();
@@ -305,17 +593,12 @@
}
@Override
- public void onLayersAvailabilityChange(List<VmsLayer> availableLayers) {
- Log.d(TAG, "onLayersAvailabilityChange: Layers: " + availableLayers);
+ public void onLayersAvailabilityChanged(List<VmsLayer> availableLayers) {
+ Log.d(TAG, "onLayersAvailabilityChanged: Layers: " + availableLayers);
mAvailableLayers.addAll(availableLayers);
mSubscriberSemaphore.release();
}
- @Override
- public void onCarDisconnected() {
-
- }
-
public VmsLayer getLayer() {
return mLayer;
}
diff --git a/tests/carservice_unit_test/Android.mk b/tests/carservice_unit_test/Android.mk
index 4c57c6e..1ec2008 100644
--- a/tests/carservice_unit_test/Android.mk
+++ b/tests/carservice_unit_test/Android.mk
@@ -34,6 +34,6 @@
LOCAL_INSTRUMENTATION_FOR := CarService
LOCAL_JAVA_LIBRARIES := android.car android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test android-support-test mockito-target-minus-junit4
include $(BUILD_PACKAGE)
diff --git a/tests/carservice_unit_test/AndroidManifest.xml b/tests/carservice_unit_test/AndroidManifest.xml
index 36b86e7..0f67275 100644
--- a/tests/carservice_unit_test/AndroidManifest.xml
+++ b/tests/carservice_unit_test/AndroidManifest.xml
@@ -18,7 +18,7 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
package="com.android.car.carservice_unittest"
android:sharedUserId="android.uid.system" >
- <instrumentation android:name="android.test.InstrumentationTestRunner"
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.car"
android:label="Unit Tests for Car APIs"/>
diff --git a/tests/carservice_unit_test/src/com/android/car/BluetoothAutoConnectPolicyTest.java b/tests/carservice_unit_test/src/com/android/car/BluetoothAutoConnectPolicyTest.java
new file mode 100644
index 0000000..85ec54f
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/BluetoothAutoConnectPolicyTest.java
@@ -0,0 +1,641 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car;
+
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothA2dpSink;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadsetClient;
+import android.bluetooth.BluetoothMapClient;
+import android.bluetooth.BluetoothPbapClient;
+import android.bluetooth.BluetoothProfile;
+import android.car.ICarUserService;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.cabin.CarCabinManager;
+import android.car.hardware.property.CarPropertyEvent;
+import android.car.CarBluetoothManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.test.AndroidTestCase;
+
+import org.mockito.Matchers;
+import org.mockito.Mockito;
+
+import static org.mockito.Mockito.*;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Unit tests for the {@link BluetoothDeviceConnectionPolicy}.
+ * Isolate and test the policy's functionality - test if it finds the right device(s) per profile to
+ * connect to when triggered by an appropriate event.
+ *
+ * The following services are mocked:
+ * 1. {@link CarBluetoothUserService} - connect requests to the Bluetooth stack are stubbed out
+ * and connection results can be injected (imitating results from the stack)
+ * 2. {@link CarCabinService} & {@link CarSensorService} - Fake vehicle events are injected to the
+ * policy's Broadcast Receiver.
+ */
+public class BluetoothAutoConnectPolicyTest extends AndroidTestCase {
+ private BluetoothDeviceConnectionPolicy mBluetoothDeviceConnectionPolicyTest;
+ private BluetoothAdapter mBluetoothAdapter;
+ private BroadcastReceiver mReceiver;
+ private BluetoothDeviceConnectionPolicy.CarPropertyListener mCabinEventListener;
+ private Handler mMainHandler;
+ private Context mockContext;
+ // Mock of Services that the policy interacts with
+ private CarCabinService mockCarCabinService;
+ private CarSensorService mockCarSensorService;
+ private CarBluetoothUserService mockBluetoothUserService;
+ private PerUserCarServiceHelper mockPerUserCarServiceHelper;
+ private ICarUserService mockPerUserCarService;
+ private CarBluetoothService mockCarBluetoothService;
+ // Timeouts
+ private static final int CONNECTION_STATE_CHANGE_TIME = 200; //ms
+ private static final int CONNECTION_REQUEST_TIMEOUT = 10000;//ms
+ private static final int WAIT_FOR_COMPLETION_TIME = 3000;//ms
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ mMainHandler = new Handler(Looper.getMainLooper());
+ makeMockServices();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mBluetoothDeviceConnectionPolicyTest.release();
+ super.tearDown();
+ }
+ /****************************************** Utility methods **********************************/
+
+ /**
+ * Pair the given device to the Car on all supported Bluetooth profiles. This is just adding a
+ * (fake) BluetoothDevice to the policy's records, which is what a real Bluetooth pairing would
+ * do.
+ */
+ private void pairDevice(BluetoothDevice device) {
+ for (Integer profile : mBluetoothDeviceConnectionPolicyTest.getProfilesToConnect()) {
+ pairDeviceOnProfile(device, profile);
+ }
+ }
+
+ /**
+ * Pair a device on the given profile. Note that this is not real Bluetooth Pairing. This is
+ * just adding a (fake) BluetoothDevice to the policy's records, which is what a real Bluetooth
+ * pairing would have done.
+ *
+ * @param device - Bluetooth device
+ * @param profile - Profile to pair on
+ */
+ private void pairDeviceOnProfile(BluetoothDevice device, Integer profile) {
+ sendFakeConnectionStateChangeOnProfile(device, profile, true);
+ }
+
+ /**
+ * Connect or Disconnect the list of devices.
+ *
+ * @param deviceList - list of Bluetooth devices
+ * @param connect - true - connect, false - disconnect
+ */
+ private void connectDevices(List<BluetoothDevice> deviceList, boolean connect) {
+ for (BluetoothDevice device : deviceList) {
+ sendFakeConnectionStateChange(device, connect);
+ }
+ }
+
+ /**
+ * Inject a fake connection state changed intent on all profiles to the policy's
+ * Broadcast Receiver
+ *
+ * @param device - Bluetooth device
+ * @param connect - connection success or failure
+ */
+ private void sendFakeConnectionStateChange(BluetoothDevice device, boolean connect) {
+ for (Integer profile : mBluetoothDeviceConnectionPolicyTest.getProfilesToConnect()) {
+ sendFakeConnectionStateChangeOnProfile(device, profile, connect);
+ }
+ }
+
+ /**
+ * Inject a fake connection state changed intent to the policy's Broadcast Receiver
+ *
+ * @param device - Bluetooth Device
+ * @param profile - Bluetooth Profile
+ * @param connect - connection Success or Failure
+ */
+ private void sendFakeConnectionStateChangeOnProfile(BluetoothDevice device, Integer profile,
+ boolean connect) {
+ assertNotNull(mReceiver);
+ Intent connectionIntent = createBluetoothConnectionStateChangedIntent(profile, device,
+ connect);
+ mReceiver.onReceive(null, connectionIntent);
+ }
+
+ /**
+ * Utility function to create a Connection State Changed Intent for the given profile and device
+ *
+ * @param profile - Bluetooth profile
+ * @param device - Bluetooth Device
+ * @param connect - Connection Success or Failure
+ * @return - Connection State Changed Intent with the filled up EXTRAs
+ */
+ private Intent createBluetoothConnectionStateChangedIntent(int profile, BluetoothDevice device,
+ boolean connect) {
+ Intent connectionIntent;
+ switch (profile) {
+ case BluetoothProfile.A2DP_SINK:
+ connectionIntent = new Intent(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
+ break;
+ case BluetoothProfile.HEADSET_CLIENT:
+ connectionIntent = new Intent(
+ BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
+ break;
+ case BluetoothProfile.MAP_CLIENT:
+ connectionIntent = new Intent(BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
+ break;
+ case BluetoothProfile.PBAP_CLIENT:
+ connectionIntent = new Intent(BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED);
+ break;
+ default:
+ return null;
+ }
+ connectionIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+ if (connect) {
+ connectionIntent.putExtra(BluetoothProfile.EXTRA_STATE,
+ BluetoothProfile.STATE_CONNECTED);
+ } else {
+ connectionIntent.putExtra(BluetoothProfile.EXTRA_STATE,
+ BluetoothProfile.STATE_DISCONNECTED);
+ }
+ return connectionIntent;
+ }
+
+ /**
+ * Trigger a fake vehicle Event by injecting directly into the policy's Event Listener.
+ * Note that a Cabin Event (Door unlock) is used here. The default policy has a Cabin Event
+ * listener.
+ * The event can be changed to what is appropriate as long as there is a corresponding listener
+ * implemented in the policy
+ */
+ private void triggerFakeVehicleEvent() throws RemoteException {
+ assertNotNull(mCabinEventListener);
+ CarPropertyValue<Boolean> value = new CarPropertyValue<>(CarCabinManager.ID_DOOR_LOCK,
+ false);
+ CarPropertyEvent event = new CarPropertyEvent(
+ CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, value);
+ mCabinEventListener.onEvent(event);
+ }
+
+ /**
+ * Put all the mock creations in one place. To be called from setup()
+ */
+ private void makeMockServices() {
+ mockContext = mock(Context.class);
+ mockCarCabinService = mock(CarCabinService.class);
+ mockCarSensorService = mock(CarSensorService.class);
+ mockPerUserCarServiceHelper = mock(PerUserCarServiceHelper.class);
+ mockPerUserCarService = mock(ICarUserService.class);
+ mockCarBluetoothService = mock(CarBluetoothService.class);
+ mockBluetoothUserService = mock(CarBluetoothUserService.class,
+ Mockito.withSettings().verboseLogging());
+ }
+
+ /**
+ * Mock response to a connection request on a specific device.
+ *
+ * @param device - Bluetooth device to mock availability
+ * @param available - result to return when a connection is requested
+ */
+ private void mockDeviceAvailability(@Nullable BluetoothDevice device, boolean available)
+ throws Exception {
+ if (mBluetoothDeviceConnectionPolicyTest != null) {
+ for (Integer profile : mBluetoothDeviceConnectionPolicyTest.getProfilesToConnect()) {
+ Mockito.doAnswer(createDeviceAvailabilityAnswer(available)).when(
+ mockBluetoothUserService).
+ bluetoothConnectToProfile(profile, device);
+ }
+ }
+ }
+
+ private Answer<Void> createDeviceAvailabilityAnswer(final boolean available) {
+ final Answer<Void> answer = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocationOnMock) throws Throwable {
+ if (invocationOnMock.getArguments().length < 2) {
+ return null;
+ }
+ final int profile = (int) invocationOnMock.getArguments()[0];
+ final BluetoothDevice device = (BluetoothDevice) invocationOnMock.getArguments()[1];
+ // Sanity check
+ if (device == null) {
+ return null;
+ }
+ mMainHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ mReceiver.onReceive(null,
+ createBluetoothConnectionStateChangedIntent(profile, device,
+ available));
+ }
+ }, CONNECTION_STATE_CHANGE_TIME);
+ return null;
+ }
+ };
+ return answer;
+ }
+
+ /**
+ * Utility method called from the beginning of every test to create and init the policy
+ */
+ private void createAndSetupBluetoothPolicy() throws Exception {
+ // Return the mock Bluetooth User Service when asked for
+ when(mockBluetoothUserService.isBluetoothConnectionProxyAvailable(
+ Matchers.anyInt())).thenReturn(true);
+ when(mockPerUserCarService.getBluetoothUserService()).thenReturn(mockBluetoothUserService);
+
+ mBluetoothDeviceConnectionPolicyTest = BluetoothDeviceConnectionPolicy.create(mockContext,
+ mockCarCabinService, mockCarSensorService, mockPerUserCarServiceHelper,
+ mockCarBluetoothService);
+ mBluetoothDeviceConnectionPolicyTest.setAllowReadWriteToSettings(false);
+ mBluetoothDeviceConnectionPolicyTest.init();
+
+ mReceiver = mBluetoothDeviceConnectionPolicyTest.getBluetoothBroadcastReceiver();
+ assertNotNull(mReceiver);
+ BluetoothDeviceConnectionPolicy.UserServiceConnectionCallback serviceConnectionCallback =
+ mBluetoothDeviceConnectionPolicyTest.getServiceCallback();
+ assertNotNull(serviceConnectionCallback);
+ mCabinEventListener = mBluetoothDeviceConnectionPolicyTest.getCarPropertyListener();
+ assertNotNull(mCabinEventListener);
+
+ serviceConnectionCallback.onServiceConnected(mockPerUserCarService);
+ }
+
+ /**
+ * Utility method called from the end of every test to cleanup and release the policy
+ */
+ private Intent createBluetoothBondStateChangedIntent(BluetoothDevice device, boolean bonded) {
+ // Unbond the device
+ Intent bondStateIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+ if (!bonded) {
+ bondStateIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
+ } else {
+ bondStateIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDED);
+ }
+ bondStateIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+ return bondStateIntent;
+ }
+
+ private void tagDeviceForAllProfiles(BluetoothDevice device, int priority) {
+ // tag device as primary in all profiles
+ for (Integer profile : mBluetoothDeviceConnectionPolicyTest.getProfilesToConnect()) {
+ tagDeviceForProfile(device, profile, priority);
+ }
+ }
+
+ private void tagDeviceForProfile(BluetoothDevice device, Integer profile, int priority) {
+ mBluetoothDeviceConnectionPolicyTest.tagDeviceWithPriority(device, profile, priority);
+ }
+
+
+ /************************************** Test Methods *****************************************/
+ /**
+ * Basic test -
+ * 1. Pair one device to the car on all profiles.
+ * 2. Disconnect the device
+ * 3. Inject a fake vehicle event
+ * 4. Verify that we get connection requests on all the profiles with that paired device
+ */
+ @Test
+ public void testAutoConnectOneDevice() throws Exception {
+ createAndSetupBluetoothPolicy();
+ // Tell the policy a new device connected - this mimics pairing
+ BluetoothDevice device1 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:01");
+ mockDeviceAvailability(device1, true);
+ // Pair (and Connect) device1 on all the Bluetooth profiles.
+ pairDevice(device1);
+ // Disconnect so we can test Autoconnect by sending a vehicle event
+ sendFakeConnectionStateChange(device1, false);
+
+ // At this point DEADBEEF0001 is paired but disconnected to the vehicle
+ // Now, trigger a connection and check if we connected to DEADBEEF0001 on all profiles
+ triggerFakeVehicleEvent();
+ // Verify that on all profiles, device1 connected
+ for (Integer profile : mBluetoothDeviceConnectionPolicyTest.getProfilesToConnect()) {
+ verify(mockBluetoothUserService,
+ Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+ profile, device1);
+ }
+
+ // Before we cleanup wait for the last Connection Status change from mockDeviceAvailability
+ // is broadcast to the policy.
+ Thread.sleep(WAIT_FOR_COMPLETION_TIME);
+ // Inject an Unbond event to the policy
+ mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device1, false));
+ }
+
+ /**
+ * Multi device test
+ * 1. Pair 4 different devices 2 on HFP and PBAP (since they allow 2 connections) and 1 each on
+ * A2DP and MAP
+ * 2. Disconnect all devices
+ * 3. Inject a fake vehicle event.
+ * 4. Verify that the right devices connect on the right profiles ( the snapshot recreated)
+ */
+ @Test
+ public void testAutoConnectMultiDevice() throws Exception {
+ createAndSetupBluetoothPolicy();
+ BluetoothDevice device1 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:01");
+ BluetoothDevice device2 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:02");
+ BluetoothDevice device3 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:03");
+ BluetoothDevice device4 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:04");
+ BluetoothDevice[] testDevices = new BluetoothDevice[]{device4, device3, device2, device1};
+
+ for (BluetoothDevice device : testDevices) {
+ mockDeviceAvailability(device, true);
+ }
+ // Pair 4 different devices on the 4 profiles. HFP and PBAP are connected on the same
+ // device(s)
+ pairDeviceOnProfile(device1, BluetoothProfile.HEADSET_CLIENT);
+ pairDeviceOnProfile(device1, BluetoothProfile.PBAP_CLIENT);
+ pairDeviceOnProfile(device2, BluetoothProfile.HEADSET_CLIENT);
+ pairDeviceOnProfile(device2, BluetoothProfile.PBAP_CLIENT);
+ pairDeviceOnProfile(device3, BluetoothProfile.A2DP_SINK);
+ pairDeviceOnProfile(device4, BluetoothProfile.MAP_CLIENT);
+
+ // Disconnect all the 4 devices on the respective connected profiles
+ sendFakeConnectionStateChangeOnProfile(device1, BluetoothProfile.HEADSET_CLIENT, false);
+ sendFakeConnectionStateChangeOnProfile(device1, BluetoothProfile.PBAP_CLIENT, false);
+ sendFakeConnectionStateChangeOnProfile(device2, BluetoothProfile.HEADSET_CLIENT, false);
+ sendFakeConnectionStateChangeOnProfile(device2, BluetoothProfile.PBAP_CLIENT, false);
+ sendFakeConnectionStateChangeOnProfile(device3, BluetoothProfile.A2DP_SINK, false);
+ sendFakeConnectionStateChangeOnProfile(device4, BluetoothProfile.MAP_CLIENT, false);
+
+ triggerFakeVehicleEvent();
+
+ verify(mockBluetoothUserService,
+ Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+ BluetoothProfile.HEADSET_CLIENT, device1);
+
+ verify(mockBluetoothUserService,
+ Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+ BluetoothProfile.PBAP_CLIENT, device1);
+ verify(mockBluetoothUserService,
+ Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+ BluetoothProfile.HEADSET_CLIENT, device2);
+ verify(mockBluetoothUserService,
+ Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+ BluetoothProfile.PBAP_CLIENT, device2);
+ verify(mockBluetoothUserService,
+ Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+ BluetoothProfile.A2DP_SINK, device3);
+ verify(mockBluetoothUserService,
+ Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+ BluetoothProfile.MAP_CLIENT, device4);
+
+ // Before we cleanup wait for the last Connection Status change from is broadcast to the
+ // policy.
+ Thread.sleep(WAIT_FOR_COMPLETION_TIME);
+ // Inject an Unbond event to the policy
+ mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device1, false));
+ mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device2, false));
+ mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device3, false));
+ mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device4, false));
+ }
+
+ /**
+ * Test setting a device as a primary device. A primary device, if present, will be the first
+ * device that the policy will try to connect regardless of which device connected last.
+ * 1. Pair 4 devices.
+ * 2. Leave them disconnected.
+ * 3. Tag one device as Primary.
+ * 4. Send a connection trigger.
+ * 5. Verify if the tagged device connected.
+ * 6. Disconnect and tag another device.
+ * 7. Send a Connection trigger.
+ * 8. Verify if the newly tagged device connected.
+ */
+ @Test
+ public void testAutoConnectSetPrimaryPriority() throws Exception {
+ createAndSetupBluetoothPolicy();
+ BluetoothDevice device1 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:01");
+ BluetoothDevice device2 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:02");
+ BluetoothDevice device3 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:03");
+ BluetoothDevice device4 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:04");
+ BluetoothDevice[] testDevices = new BluetoothDevice[]{device4, device3, device2, device1};
+
+ // pair all 4 devices on all 4 profiles
+ for (BluetoothDevice device : testDevices) {
+ mockDeviceAvailability(device, true);
+ pairDevice(device);
+ // Disconnect the device. We want to test auto connection, so the state we want
+ // to be at the end of this loop is paired and disconnected
+ for (Integer profile : mBluetoothDeviceConnectionPolicyTest.getProfilesToConnect()) {
+ sendFakeConnectionStateChangeOnProfile(device, profile, false);
+ }
+ }
+ // Device Order for all profiles will be {device1, device2, device3, device4} in the order
+ // of who connected last.
+ // Randomly pick device3 as the primary device
+ tagDeviceForAllProfiles(device3,
+ CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0);
+ // Device order should be {device3, device1, device2, device4} now
+ // Now when we trigger an auto connect, device 3 should connect on all profiles
+ triggerFakeVehicleEvent();
+ verify(mockBluetoothUserService,
+ Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+ BluetoothProfile.HEADSET_CLIENT, device3);
+ verify(mockBluetoothUserService,
+ Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+ BluetoothProfile.PBAP_CLIENT, device3);
+ verify(mockBluetoothUserService,
+ Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+ BluetoothProfile.A2DP_SINK, device3);
+ verify(mockBluetoothUserService,
+ Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+ BluetoothProfile.MAP_CLIENT, device3);
+ Thread.sleep(WAIT_FOR_COMPLETION_TIME);
+
+ // Change primary device to device4
+ tagDeviceForAllProfiles(device4,
+ CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0);
+ //Disconnect on all 4 profiles. device3 is connected on all profiles. device1 on HFP & PBAP
+ sendFakeConnectionStateChange(device3, false);
+ sendFakeConnectionStateChangeOnProfile(device1, BluetoothProfile.HEADSET_CLIENT, false);
+ sendFakeConnectionStateChangeOnProfile(device1, BluetoothProfile.PBAP_CLIENT, false);
+
+ // Device Order should be {device4, device3, device1, device2}
+ triggerFakeVehicleEvent();
+
+ // Check if device4 connects now
+ verify(mockBluetoothUserService,
+ Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+ BluetoothProfile.HEADSET_CLIENT, device4);
+ verify(mockBluetoothUserService,
+ Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+ BluetoothProfile.PBAP_CLIENT, device4);
+ verify(mockBluetoothUserService,
+ Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+ BluetoothProfile.A2DP_SINK, device4);
+ verify(mockBluetoothUserService,
+ Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+ BluetoothProfile.MAP_CLIENT, device4);
+
+ // Before we cleanup wait for the last Connection Status change is broadcast to the policy.
+ Thread.sleep(WAIT_FOR_COMPLETION_TIME);
+ // Inject an Unbond event to the policy
+ mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device1, false));
+ mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device2, false));
+ mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device3, false));
+ mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device4, false));
+ }
+
+ /**
+ * Test setting a device with Secondary priority.
+ * Tag a device as primary and another as secondary.
+ * Choose a profile that supports only one active connection - A2DP for example.
+ * Mock all devices to be available for connection - connection requests are successful
+ * Trigger connection.
+ * Secondary device should not have connected on A2DP (since Primary is available)
+ * Change Primary device to unavailable.
+ * Trigger Connection.
+ * Secondary device should now be connected on A2DP (since Primary is not available)
+ */
+ @Test
+ public void testAutoConnectSetSecondaryPriority() throws Exception {
+ createAndSetupBluetoothPolicy();
+ BluetoothDevice device1 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:01");
+ BluetoothDevice device2 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:02");
+ BluetoothDevice device3 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:03");
+ BluetoothDevice device4 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:04");
+ BluetoothDevice[] testDevices = new BluetoothDevice[]{device4, device3, device2, device1};
+
+ // Mark all devices to respond successfully for a connection request & Pair them
+ for (BluetoothDevice device : testDevices) {
+ mockDeviceAvailability(device, true);
+ pairDevice(device);
+ }
+ connectDevices(Arrays.asList(testDevices), false);
+ // Device order at this point {device1, device2, device3, device4} in the order of last
+ // connected first
+
+ // Tag device3 as Primary device and device4 as Secondary device.
+ tagDeviceForAllProfiles(device3,
+ CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_0);
+ tagDeviceForAllProfiles(device4,
+ CarBluetoothManager.BLUETOOTH_DEVICE_CONNECTION_PRIORITY_1);
+ // Device order at this point {device3, device4, device1, device2}
+ // Test 1:
+ // a) When a connection event triggers, no connection attempt should have been made on
+ // device4 for A2DPS_SINK profile because:
+ // 1. A2DP_SINK supports only one active connection.
+ // 2. Device1 is the first device in the order and it's available for connection
+ // b) Connection attempt should have been made on HFP since HFP supports 2 connections and
+ // device4 is the second device.
+ triggerFakeVehicleEvent();
+ // Mockito.never() doesn't have a timeout option, hence the sleep here.
+ Thread.sleep(WAIT_FOR_COMPLETION_TIME);
+ verify(mockBluetoothUserService, Mockito.never()).bluetoothConnectToProfile(
+ BluetoothProfile.A2DP_SINK, device4);
+ verify(mockBluetoothUserService,
+ Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+ BluetoothProfile.HEADSET_CLIENT, device4);
+
+ // Disconnect devices again
+ connectDevices(Arrays.asList(testDevices), false);
+ // Mock primaryDevice to be unavailable
+ mockDeviceAvailability(device3, false);
+ triggerFakeVehicleEvent();
+ // Now a connection attempt should be made on device4 for A2DP_SINK for the same reasons as
+ // above
+ verify(mockBluetoothUserService,
+ Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+ BluetoothProfile.A2DP_SINK, device4);
+ Thread.sleep(WAIT_FOR_COMPLETION_TIME);
+ mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device1, false));
+ mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device2, false));
+ mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device3, false));
+ mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device4, false));
+ }
+
+ /**
+ * When 2 devices are paired and one is connected, test if the second device when brought in
+ * range connects when a vehicle event occurs.
+ * 1. Pair 2 devices.
+ * 2. Keep one connected.
+ * 3. Bring the other device.
+ * 4. Send a vehicle event.
+ * 5. Test if the second device connects (in addition to the already connected first device)
+ * @throws Exception
+ */
+ @Test
+ public void testMultiDeviceConnectWithOneConnected() throws Exception {
+ createAndSetupBluetoothPolicy();
+ BluetoothDevice device1 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:01");
+ BluetoothDevice device2 = mBluetoothAdapter.getRemoteDevice("DE:AD:BE:EF:00:02");
+ BluetoothDevice[] testDevices = new BluetoothDevice[]{device2, device1};
+
+ // Pair both devices and leave them disconnected.
+ for (BluetoothDevice device : testDevices) {
+ mockDeviceAvailability(device, true);
+ pairDevice(device);
+ sendFakeConnectionStateChange(device, false);
+ }
+
+ // Mock the second device to be unavailable for connection.
+ mockDeviceAvailability(device2, false);
+ triggerFakeVehicleEvent();
+ Thread.sleep(WAIT_FOR_COMPLETION_TIME);
+ // At this point device1 should have connected and device2 disconnected, since it is mocked
+ // to be out of range (unavailable)
+ // Now bring device2 in range (mock it to be available)
+ mockDeviceAvailability(device2, true);
+ triggerFakeVehicleEvent();
+ Thread.sleep(WAIT_FOR_COMPLETION_TIME);
+
+ // Now device2 should be connected on the HFP, but not on A2DP (since it supports only
+ // 1 connection)
+ // There should have been 2 connection attempts on the device2 - the first one unsuccessful
+ // due to its unavailability and the second one successful.
+ verify(mockBluetoothUserService,
+ Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(2)).bluetoothConnectToProfile(
+ BluetoothProfile.HEADSET_CLIENT, device2);
+ // There should be only 1 connection attempt on device1 - since it is available to connect
+ // from the beginning. The first connection attempt on the first vehicle event should have
+ // been successful. For the second vehicle event, we should not have tried to connect on
+ // device1 - this tests if we try to connect on already connected devices.
+ verify(mockBluetoothUserService,
+ Mockito.timeout(CONNECTION_REQUEST_TIMEOUT).times(1)).bluetoothConnectToProfile(
+ BluetoothProfile.HEADSET_CLIENT, device1);
+ verify(mockBluetoothUserService, Mockito.never()).bluetoothConnectToProfile(
+ BluetoothProfile.A2DP_SINK, device2);
+ Thread.sleep(WAIT_FOR_COMPLETION_TIME);
+ mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device1, false));
+ mReceiver.onReceive(null, createBluetoothBondStateChangedIntent(device2, false));
+ }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/VmsLayersAvailabilityTest.java b/tests/carservice_unit_test/src/com/android/car/VmsLayersAvailabilityTest.java
index d6fd68d..4a09d4f 100644
--- a/tests/carservice_unit_test/src/com/android/car/VmsLayersAvailabilityTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/VmsLayersAvailabilityTest.java
@@ -16,6 +16,7 @@
package com.android.car;
+import android.car.vms.VmsAssociatedLayer;
import android.car.vms.VmsLayer;
import android.car.vms.VmsLayerDependency;
import android.car.vms.VmsLayersOffering;
@@ -30,33 +31,41 @@
@SmallTest
public class VmsLayersAvailabilityTest extends AndroidTestCase {
- private static final VmsLayer LAYER_X = new VmsLayer(1, 2);
- private static final VmsLayer LAYER_Y = new VmsLayer(3, 4);
- private static final VmsLayer LAYER_Z = new VmsLayer(5, 6);
+ private static final VmsLayer LAYER_X = new VmsLayer(1, 1, 2);
+ private static final VmsLayer LAYER_Y = new VmsLayer(3, 2, 4);
+ private static final VmsLayer LAYER_Z = new VmsLayer(5, 3, 6);
+
+ private static final int PUBLISHER_ID_1 = 19;
+ private static final int PUBLISHER_ID_2 = 28;
+
+ private static final Set<Integer> PUBLISHERS_1 = new HashSet<>(Arrays.asList(PUBLISHER_ID_1));
+ private static final Set<Integer> PUBLISHERS_2 = new HashSet<>(Arrays.asList(PUBLISHER_ID_2));
+ private static final Set<Integer> PUBLISHERS_1_AND_2 =
+ new HashSet<>(Arrays.asList(PUBLISHER_ID_1, PUBLISHER_ID_2));
private static final VmsLayerDependency X_DEPENDS_ON_Y =
- new VmsLayerDependency(LAYER_X, new HashSet<VmsLayer>(Arrays.asList(LAYER_Y)));
+ new VmsLayerDependency(LAYER_X, new HashSet<VmsLayer>(Arrays.asList(LAYER_Y)));
private static final VmsLayerDependency X_DEPENDS_ON_Z =
- new VmsLayerDependency(LAYER_X, new HashSet<VmsLayer>(Arrays.asList(LAYER_Z)));
+ new VmsLayerDependency(LAYER_X, new HashSet<VmsLayer>(Arrays.asList(LAYER_Z)));
private static final VmsLayerDependency Y_DEPENDS_ON_Z =
- new VmsLayerDependency(LAYER_Y, new HashSet<VmsLayer>(Arrays.asList(LAYER_Z)));
+ new VmsLayerDependency(LAYER_Y, new HashSet<VmsLayer>(Arrays.asList(LAYER_Z)));
private static final VmsLayerDependency Y_DEPENDS_ON_X =
- new VmsLayerDependency(LAYER_Y, new HashSet<VmsLayer>(Arrays.asList(LAYER_X)));
+ new VmsLayerDependency(LAYER_Y, new HashSet<VmsLayer>(Arrays.asList(LAYER_X)));
private static final VmsLayerDependency Z_DEPENDS_ON_X =
- new VmsLayerDependency(LAYER_Z, new HashSet<VmsLayer>(Arrays.asList(LAYER_X)));
+ new VmsLayerDependency(LAYER_Z, new HashSet<VmsLayer>(Arrays.asList(LAYER_X)));
private static final VmsLayerDependency Z_DEPENDS_ON_NOTHING =
- new VmsLayerDependency(LAYER_Z);
+ new VmsLayerDependency(LAYER_Z);
private static final VmsLayerDependency X_DEPENDS_ON_SELF =
- new VmsLayerDependency(LAYER_X, new HashSet<VmsLayer>(Arrays.asList(LAYER_X)));
+ new VmsLayerDependency(LAYER_X, new HashSet<VmsLayer>(Arrays.asList(LAYER_X)));
private Set<VmsLayersOffering> mOfferings;
- private VmsLayersAvailability mLayersAvailability;
+ private VmsLayersAvailability mLayersAvailability;
@Override
protected void setUp() throws Exception {
@@ -75,184 +84,183 @@
}
public void testSingleLayerNoDeps() throws Exception {
- Set<VmsLayer> expectedAvailableLayers = new HashSet<>();
- expectedAvailableLayers.add(LAYER_X);
+ Set<VmsAssociatedLayer> expectedAvailableAssociatedLayers = new HashSet<>();
+ expectedAvailableAssociatedLayers.add(new VmsAssociatedLayer(LAYER_X, PUBLISHERS_2));
VmsLayersOffering offering =
- new VmsLayersOffering(Arrays.asList(new VmsLayerDependency(LAYER_X)));
+ new VmsLayersOffering(new HashSet<>(Arrays.asList(new VmsLayerDependency(LAYER_X))),
+ PUBLISHER_ID_2);
mOfferings.add(offering);
mLayersAvailability.setPublishersOffering(mOfferings);
- assertEquals(expectedAvailableLayers, mLayersAvailability.getAvailableLayers());
+ assertEquals(expectedAvailableAssociatedLayers, mLayersAvailability.getAvailableLayers());
}
public void testChainOfDependenciesSatisfied() throws Exception {
- Set<VmsLayer> expectedAvailableLayers = new HashSet<>();
- expectedAvailableLayers.add(LAYER_X);
- expectedAvailableLayers.add(LAYER_Y);
- expectedAvailableLayers.add(LAYER_Z);
+ Set<VmsAssociatedLayer> expectedAvailableAssociatedLayers = new HashSet<>();
+ expectedAvailableAssociatedLayers.add(new VmsAssociatedLayer(LAYER_X, PUBLISHERS_1));
+ expectedAvailableAssociatedLayers.add(new VmsAssociatedLayer(LAYER_Y, PUBLISHERS_1));
+ expectedAvailableAssociatedLayers.add(new VmsAssociatedLayer(LAYER_Z, PUBLISHERS_1));
VmsLayersOffering offering =
- new VmsLayersOffering(Arrays.asList(
- X_DEPENDS_ON_Y,
- Y_DEPENDS_ON_Z,
- Z_DEPENDS_ON_NOTHING));
+ new VmsLayersOffering(
+ new HashSet<>(Arrays.asList(X_DEPENDS_ON_Y, Y_DEPENDS_ON_Z, Z_DEPENDS_ON_NOTHING)),
+ PUBLISHER_ID_1);
mOfferings.add(offering);
mLayersAvailability.setPublishersOffering(mOfferings);
- assertEquals(expectedAvailableLayers,
- new HashSet<VmsLayer>(mLayersAvailability.getAvailableLayers()));
+ assertEquals(expectedAvailableAssociatedLayers,
+ new HashSet<VmsAssociatedLayer>(mLayersAvailability.getAvailableLayers()));
}
public void testChainOfDependenciesSatisfiedTwoOfferings() throws Exception {
- Set<VmsLayer> expectedAvailableLayers = new HashSet<>();
- expectedAvailableLayers.add(LAYER_X);
- expectedAvailableLayers.add(LAYER_Y);
- expectedAvailableLayers.add(LAYER_Z);
+ Set<VmsAssociatedLayer> expectedAvailableAssociatedLayers = new HashSet<>();
+ expectedAvailableAssociatedLayers.add(new VmsAssociatedLayer(LAYER_X, PUBLISHERS_1));
+ expectedAvailableAssociatedLayers.add(new VmsAssociatedLayer(LAYER_Y, PUBLISHERS_1));
+ expectedAvailableAssociatedLayers.add(new VmsAssociatedLayer(LAYER_Z, PUBLISHERS_1));
VmsLayersOffering offering1 =
- new VmsLayersOffering(Arrays.asList(
- X_DEPENDS_ON_Y,
- Y_DEPENDS_ON_Z));
+ new VmsLayersOffering(
+ new HashSet<>(Arrays.asList(X_DEPENDS_ON_Y, Y_DEPENDS_ON_Z)),
+ PUBLISHER_ID_1);
VmsLayersOffering offering2 =
- new VmsLayersOffering(Arrays.asList(
- Z_DEPENDS_ON_NOTHING));
+ new VmsLayersOffering(new HashSet<>(Arrays.asList(Z_DEPENDS_ON_NOTHING)),
+ PUBLISHER_ID_1);
mOfferings.add(offering1);
mOfferings.add(offering2);
mLayersAvailability.setPublishersOffering(mOfferings);
- assertEquals(expectedAvailableLayers,
- new HashSet<VmsLayer>(mLayersAvailability.getAvailableLayers()));
+ assertEquals(expectedAvailableAssociatedLayers,
+ new HashSet<VmsAssociatedLayer>(mLayersAvailability.getAvailableLayers()));
}
public void testChainOfDependencieNotSatisfied() throws Exception {
- Set<VmsLayer> expectedAvailableLayers = new HashSet<>();
- VmsLayersOffering offering =new VmsLayersOffering(Arrays.asList(
- X_DEPENDS_ON_Y,
- Y_DEPENDS_ON_Z));
+ Set<VmsAssociatedLayer> expectedAvailableAssociatedLayers = new HashSet<>();
+ VmsLayersOffering offering =
+ new VmsLayersOffering(new HashSet<>(Arrays.asList(X_DEPENDS_ON_Y, Y_DEPENDS_ON_Z)),
+ PUBLISHER_ID_1);
mOfferings.add(offering);
mLayersAvailability.setPublishersOffering(mOfferings);
- assertEquals(expectedAvailableLayers,
- new HashSet<VmsLayer>(mLayersAvailability.getAvailableLayers()));
+ assertEquals(expectedAvailableAssociatedLayers,
+ new HashSet<VmsAssociatedLayer>(mLayersAvailability.getAvailableLayers()));
- Set<VmsLayer> expectedUnavailableLayers = new HashSet<>();
- expectedUnavailableLayers.add(LAYER_X);
- expectedUnavailableLayers.add(LAYER_Y);
+ Set<VmsAssociatedLayer> expectedUnavailableAssociatedLayers = new HashSet<>();
+ expectedUnavailableAssociatedLayers.add(new VmsAssociatedLayer(LAYER_X, PUBLISHERS_1));
+ expectedUnavailableAssociatedLayers.add(new VmsAssociatedLayer(LAYER_Y, PUBLISHERS_1));
- assertEquals(expectedUnavailableLayers ,
- new HashSet<VmsLayer>(mLayersAvailability.getUnavailableLayers()));
+
+ assertEquals(expectedUnavailableAssociatedLayers,
+ new HashSet<VmsAssociatedLayer>(mLayersAvailability.getUnavailableLayers()));
}
public void testOneOfMultipleDependencySatisfied() throws Exception {
- Set<VmsLayer> expectedAvailableLayers = new HashSet<>();
- expectedAvailableLayers.add(LAYER_X);
- expectedAvailableLayers.add(LAYER_Z);
+ Set<VmsAssociatedLayer> expectedAvailableAssociatedLayers = new HashSet<>();
+ expectedAvailableAssociatedLayers.add(new VmsAssociatedLayer(LAYER_X, PUBLISHERS_1));
+ expectedAvailableAssociatedLayers.add(new VmsAssociatedLayer(LAYER_Z, PUBLISHERS_1));
+
VmsLayersOffering offering =
- new VmsLayersOffering(Arrays.asList(
- X_DEPENDS_ON_Y,
- X_DEPENDS_ON_Z,
- Z_DEPENDS_ON_NOTHING));
+ new VmsLayersOffering(
+ new HashSet<>(Arrays.asList(
+ X_DEPENDS_ON_Y, X_DEPENDS_ON_Z, Z_DEPENDS_ON_NOTHING)),
+ PUBLISHER_ID_1);
mOfferings.add(offering);
mLayersAvailability.setPublishersOffering(mOfferings);
- assertEquals(expectedAvailableLayers,
- new HashSet<VmsLayer>(mLayersAvailability.getAvailableLayers()));
+ assertEquals(expectedAvailableAssociatedLayers,
+ new HashSet<VmsAssociatedLayer>(mLayersAvailability.getAvailableLayers()));
}
public void testCyclicDependency() throws Exception {
- Set<VmsLayer> expectedAvailableLayers = new HashSet<>();
+ Set<VmsAssociatedLayer> expectedAvailableAssociatedLayers = new HashSet<>();
VmsLayersOffering offering =
- new VmsLayersOffering(Arrays.asList(
- X_DEPENDS_ON_Y,
- Y_DEPENDS_ON_Z,
- Z_DEPENDS_ON_X));
+ new VmsLayersOffering(
+ new HashSet<>(
+ Arrays.asList(X_DEPENDS_ON_Y, Y_DEPENDS_ON_Z, Z_DEPENDS_ON_X)),
+ PUBLISHER_ID_1);
mOfferings.add(offering);
mLayersAvailability.setPublishersOffering(mOfferings);
- assertEquals(expectedAvailableLayers,
- new HashSet<VmsLayer>(mLayersAvailability.getAvailableLayers()));
+ assertEquals(expectedAvailableAssociatedLayers,
+ new HashSet<VmsAssociatedLayer>(mLayersAvailability.getAvailableLayers()));
}
public void testAlmostCyclicDependency() throws Exception {
- Set<VmsLayer> expectedAvailableLayers = new HashSet<>();
- expectedAvailableLayers.add(LAYER_X);
- expectedAvailableLayers.add(LAYER_Y);
- expectedAvailableLayers.add(LAYER_Z);
+ Set<VmsAssociatedLayer> expectedAvailableAssociatedLayers = new HashSet<>();
+ expectedAvailableAssociatedLayers.add(new VmsAssociatedLayer(LAYER_Z, PUBLISHERS_1_AND_2));
+ expectedAvailableAssociatedLayers.add(new VmsAssociatedLayer(LAYER_X, PUBLISHERS_1));
+ expectedAvailableAssociatedLayers.add(new VmsAssociatedLayer(LAYER_Y, PUBLISHERS_2));
VmsLayersOffering offering1 =
- new VmsLayersOffering(Arrays.asList(
- X_DEPENDS_ON_Y,
- Z_DEPENDS_ON_NOTHING));
+ new VmsLayersOffering(
+ new HashSet<>(Arrays.asList(X_DEPENDS_ON_Y, Z_DEPENDS_ON_NOTHING)),
+ PUBLISHER_ID_1);
VmsLayersOffering offering2 =
- new VmsLayersOffering(Arrays.asList(
- Y_DEPENDS_ON_Z,
- Z_DEPENDS_ON_X));
+ new VmsLayersOffering(new HashSet<>(Arrays.asList(Y_DEPENDS_ON_Z, Z_DEPENDS_ON_X)),
+ PUBLISHER_ID_2);
mOfferings.add(offering1);
mOfferings.add(offering2);
mLayersAvailability.setPublishersOffering(mOfferings);
- assertEquals(expectedAvailableLayers,
- new HashSet<VmsLayer>(mLayersAvailability.getAvailableLayers()));
+ assertEquals(expectedAvailableAssociatedLayers, mLayersAvailability.getAvailableLayers());
}
public void testCyclicDependencyAndLayerWithoutDependency() throws Exception {
- Set<VmsLayer> expectedAvailableLayers = new HashSet<>();
- expectedAvailableLayers.add(LAYER_Z);
+ Set<VmsAssociatedLayer> expectedAvailableAssociatedLayers = new HashSet<>();
+ expectedAvailableAssociatedLayers.add(new VmsAssociatedLayer(LAYER_Z, PUBLISHERS_1));
VmsLayersOffering offering1 =
- new VmsLayersOffering(Arrays.asList(
- X_DEPENDS_ON_Y,
- Z_DEPENDS_ON_NOTHING));
+ new VmsLayersOffering(
+ new HashSet<>(
+ Arrays.asList(X_DEPENDS_ON_Y, Z_DEPENDS_ON_NOTHING)),
+ PUBLISHER_ID_1);
VmsLayersOffering offering2 =
- new VmsLayersOffering(Arrays.asList(
- Y_DEPENDS_ON_X));
+ new VmsLayersOffering(new HashSet<>(Arrays.asList(Y_DEPENDS_ON_X)), PUBLISHER_ID_2);
mOfferings.add(offering1);
mOfferings.add(offering2);
mLayersAvailability.setPublishersOffering(mOfferings);
- assertEquals(expectedAvailableLayers,
- new HashSet<VmsLayer>(mLayersAvailability.getAvailableLayers()));
+ assertEquals(expectedAvailableAssociatedLayers,
+ new HashSet<VmsAssociatedLayer>(mLayersAvailability.getAvailableLayers()));
+ Set<VmsAssociatedLayer> expectedUnavailableAssociatedLayers = new HashSet<>();
+ expectedUnavailableAssociatedLayers.add(new VmsAssociatedLayer(LAYER_X, PUBLISHERS_1));
+ expectedUnavailableAssociatedLayers.add(new VmsAssociatedLayer(LAYER_Y, PUBLISHERS_2));
- Set<VmsLayer> expectedUnavailableLayers = new HashSet<>();
- expectedUnavailableLayers.add(LAYER_Y);
- expectedUnavailableLayers.add(LAYER_X);
-
- assertEquals(expectedUnavailableLayers,
- new HashSet<VmsLayer>(mLayersAvailability.getUnavailableLayers()));
+ assertEquals(expectedUnavailableAssociatedLayers,
+ new HashSet<VmsAssociatedLayer>(mLayersAvailability.getUnavailableLayers()));
}
public void testSelfDependency() throws Exception {
- Set<VmsLayer> expectedAvailableLayers = new HashSet<>();
+ Set<VmsAssociatedLayer> expectedAvailableAssociatedLayers = new HashSet<>();
VmsLayersOffering offering =
- new VmsLayersOffering(Arrays.asList(
- X_DEPENDS_ON_SELF));
+ new VmsLayersOffering(new HashSet<>(Arrays.asList(X_DEPENDS_ON_SELF)),
+ PUBLISHER_ID_1);
mOfferings.add(offering);
mLayersAvailability.setPublishersOffering(mOfferings);
- assertEquals(expectedAvailableLayers,
- new HashSet<VmsLayer>(mLayersAvailability.getAvailableLayers()));
+ assertEquals(expectedAvailableAssociatedLayers,
+ new HashSet<VmsAssociatedLayer>(mLayersAvailability.getAvailableLayers()));
- Set<VmsLayer> expectedUnavailableLayers = new HashSet<>();
- expectedUnavailableLayers.add(LAYER_X);
+ Set<VmsAssociatedLayer> expectedUnavailableAssociatedLayers = new HashSet<>();
+ expectedUnavailableAssociatedLayers.add(new VmsAssociatedLayer(LAYER_X, PUBLISHERS_1));
- assertEquals(expectedUnavailableLayers,
- new HashSet<VmsLayer>(mLayersAvailability.getUnavailableLayers()));
+ assertEquals(expectedUnavailableAssociatedLayers,
+ new HashSet<VmsAssociatedLayer>(mLayersAvailability.getUnavailableLayers()));
}
}
\ No newline at end of file
diff --git a/tests/carservice_unit_test/src/com/android/car/VmsRoutingTest.java b/tests/carservice_unit_test/src/com/android/car/VmsRoutingTest.java
index 0ff1c85..8fe849b 100644
--- a/tests/carservice_unit_test/src/com/android/car/VmsRoutingTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/VmsRoutingTest.java
@@ -17,20 +17,29 @@
package com.android.car;
import android.car.vms.IVmsSubscriberClient;
+import android.car.vms.VmsAssociatedLayer;
import android.car.vms.VmsLayer;
import android.car.vms.VmsSubscriptionState;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.Map;
@SmallTest
public class VmsRoutingTest extends AndroidTestCase {
- private static VmsLayer LAYER_WITH_SUBSCRIPTION_1= new VmsLayer(1, 2);
- private static VmsLayer LAYER_WITH_SUBSCRIPTION_2= new VmsLayer(1, 3);
- private static VmsLayer LAYER_WITHOUT_SUBSCRIPTION= new VmsLayer(1, 4);
+ private static VmsLayer LAYER_WITH_SUBSCRIPTION_1 = new VmsLayer(1, 1, 2);
+ private static VmsLayer LAYER_WITH_SUBSCRIPTION_2 = new VmsLayer(1, 3, 3);
+ private static VmsLayer LAYER_WITHOUT_SUBSCRIPTION =
+ new VmsLayer(1, 7, 4);
+ private static int PUBLISHER_ID_1 = 123;
+ private static int PUBLISHER_ID_2 = 456;
+ private static int PUBLISHER_ID_UNLISTED = 789;
private VmsRouting mRouting;
@Override
@@ -41,8 +50,8 @@
public void testAddingSubscribersAndHalLayersNoOverlap() throws Exception {
// Add a subscription to a layer.
- MockVmsListener listener = new MockVmsListener();
- mRouting.addSubscription(listener, LAYER_WITH_SUBSCRIPTION_1);
+ MockVmsSubscriber subscriber = new MockVmsSubscriber();
+ mRouting.addSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_1);
// Add a HAL subscription.
mRouting.addHalSubscription(LAYER_WITH_SUBSCRIPTION_2);
@@ -53,18 +62,20 @@
expectedSubscriptions.add(LAYER_WITH_SUBSCRIPTION_2);
VmsSubscriptionState subscriptionState = mRouting.getSubscriptionState();
assertEquals(2, subscriptionState.getSequenceNumber());
- assertEquals(expectedSubscriptions, new HashSet<>(subscriptionState.getLayers()));
+ assertEquals(expectedSubscriptions,
+ new HashSet<>(subscriptionState.getLayers()));
- // Verify there is only a single listener.
+ // Verify there is only a single subscriber.
assertEquals(1,
- mRouting.getListeners(LAYER_WITH_SUBSCRIPTION_1).size());
+ mRouting.getSubscribersForLayerFromPublisher(
+ LAYER_WITH_SUBSCRIPTION_1, PUBLISHER_ID_1).size());
}
public void testAddingSubscribersAndHalLayersWithOverlap() throws Exception {
// Add a subscription to a layer.
- MockVmsListener listener = new MockVmsListener();
- mRouting.addSubscription(listener, LAYER_WITH_SUBSCRIPTION_1);
- mRouting.addSubscription(listener, LAYER_WITH_SUBSCRIPTION_2);
+ MockVmsSubscriber subscriber = new MockVmsSubscriber();
+ mRouting.addSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_1);
+ mRouting.addSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_2);
// Add a HAL subscription to a layer there is already another subscriber for.
mRouting.addHalSubscription(LAYER_WITH_SUBSCRIPTION_2);
@@ -75,19 +86,20 @@
expectedSubscriptions.add(LAYER_WITH_SUBSCRIPTION_2);
VmsSubscriptionState subscriptionState = mRouting.getSubscriptionState();
assertEquals(3, subscriptionState.getSequenceNumber());
- assertEquals(expectedSubscriptions, new HashSet<>(subscriptionState.getLayers()));
+ assertEquals(expectedSubscriptions,
+ new HashSet<>(subscriptionState.getLayers()));
}
public void testAddingAndRemovingLayers() throws Exception {
// Add a subscription to a layer.
- MockVmsListener listener = new MockVmsListener();
- mRouting.addSubscription(listener, LAYER_WITH_SUBSCRIPTION_1);
+ MockVmsSubscriber subscriber = new MockVmsSubscriber();
+ mRouting.addSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_1);
// Add a HAL subscription.
mRouting.addHalSubscription(LAYER_WITH_SUBSCRIPTION_2);
// Remove a subscription to a layer.
- mRouting.removeSubscription(listener, LAYER_WITH_SUBSCRIPTION_1);
+ mRouting.removeSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_1);
// Update the HAL subscription
mRouting.removeHalSubscription(LAYER_WITH_SUBSCRIPTION_2);
@@ -100,46 +112,198 @@
public void testAddingBothTypesOfSubscribers() throws Exception {
// Add a subscription to a layer.
- MockVmsListener listenerForLayer = new MockVmsListener();
- mRouting.addSubscription(listenerForLayer, LAYER_WITH_SUBSCRIPTION_1);
+ MockVmsSubscriber subscriberForLayer = new MockVmsSubscriber();
+ mRouting.addSubscription(subscriberForLayer, LAYER_WITH_SUBSCRIPTION_1);
// Add a subscription without a layer.
- MockVmsListener listenerWithoutLayer = new MockVmsListener();
- mRouting.addSubscription(listenerWithoutLayer );
+ MockVmsSubscriber subscriberWithoutLayer = new MockVmsSubscriber();
+ mRouting.addSubscription(subscriberWithoutLayer);
// Verify 2 subscribers for the layer.
assertEquals(2,
- mRouting.getListeners(LAYER_WITH_SUBSCRIPTION_1).size());
+ mRouting.getSubscribersForLayerFromPublisher(
+ LAYER_WITH_SUBSCRIPTION_1, PUBLISHER_ID_1).size());
- // Add the listener with layer as also a listener without layer
- mRouting.addSubscription(listenerForLayer);
+ // Add the subscriber with layer as also a subscriber without layer
+ mRouting.addSubscription(subscriberForLayer);
- // The number of listeners for the layer should remain the same as before.
+ // The number of subscribers for the layer should remain the same as before.
assertEquals(2,
- mRouting.getListeners(LAYER_WITH_SUBSCRIPTION_1).size());
+ mRouting.getSubscribersForLayerFromPublisher(
+ LAYER_WITH_SUBSCRIPTION_1, PUBLISHER_ID_1).size());
}
public void testOnlyRelevantSubscribers() throws Exception {
// Add a subscription to a layer.
- MockVmsListener listenerForLayer = new MockVmsListener();
- mRouting.addSubscription(listenerForLayer, LAYER_WITH_SUBSCRIPTION_1);
+ MockVmsSubscriber subscriberForLayer = new MockVmsSubscriber();
+ mRouting.addSubscription(subscriberForLayer, LAYER_WITH_SUBSCRIPTION_1);
// Add a subscription without a layer.
- MockVmsListener listenerWithoutLayer = new MockVmsListener();
- mRouting.addSubscription(listenerWithoutLayer);
+ MockVmsSubscriber subscriberWithoutLayer = new MockVmsSubscriber();
+ mRouting.addSubscription(subscriberWithoutLayer);
// Verify that only the subscriber without layer is returned.
- Set<MockVmsListener> expectedListeneres = new HashSet<MockVmsListener>();
- expectedListeneres.add(listenerWithoutLayer);
+ Set<MockVmsSubscriber> expectedListeneres = new HashSet<MockVmsSubscriber>();
+ expectedListeneres.add(subscriberWithoutLayer);
assertEquals(expectedListeneres,
- mRouting.getListeners(LAYER_WITHOUT_SUBSCRIPTION));
+ mRouting.getSubscribersForLayerFromPublisher(
+ LAYER_WITHOUT_SUBSCRIPTION, PUBLISHER_ID_1));
}
- class MockVmsListener extends IVmsSubscriberClient.Stub {
+ public void testAddingSubscribersAndHalLayersAndSubscribersToPublishers() throws Exception {
+ // Add a subscription to a layer.
+ MockVmsSubscriber subscriber = new MockVmsSubscriber();
+ mRouting.addSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_1);
+
+ // Add a HAL subscription.
+ mRouting.addHalSubscription(LAYER_WITH_SUBSCRIPTION_2);
+
+ mRouting.addSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_1, PUBLISHER_ID_1);
+ mRouting.addSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_1, PUBLISHER_ID_2);
+ mRouting.addSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_2, PUBLISHER_ID_2);
+
+ // Verify expected subscriptions are in routing manager.
+ Set<VmsLayer> expectedSubscriptions = new HashSet<>();
+ expectedSubscriptions.add(LAYER_WITH_SUBSCRIPTION_1);
+ expectedSubscriptions.add(LAYER_WITH_SUBSCRIPTION_2);
+
+ Set<VmsAssociatedLayer> expectedSubscriptionsToPublishers = new HashSet<>();
+ expectedSubscriptionsToPublishers.add(new VmsAssociatedLayer(LAYER_WITH_SUBSCRIPTION_1,
+ new HashSet(Arrays.asList(PUBLISHER_ID_1, PUBLISHER_ID_2))));
+ expectedSubscriptionsToPublishers.add(new VmsAssociatedLayer(LAYER_WITH_SUBSCRIPTION_2,
+ new HashSet(Arrays.asList(PUBLISHER_ID_2))));
+
+ VmsSubscriptionState subscriptionState = mRouting.getSubscriptionState();
+ assertEquals(5, subscriptionState.getSequenceNumber());
+ assertEquals(expectedSubscriptions,
+ new HashSet<>(subscriptionState.getLayers()));
+
+ assertEquals(expectedSubscriptionsToPublishers,
+ subscriptionState.getAssociatedLayers());
+
+ // Verify there is only a single subscriber.
+ assertEquals(1,
+ mRouting.getSubscribersForLayerFromPublisher(
+ LAYER_WITH_SUBSCRIPTION_1, PUBLISHER_ID_1).size());
+ }
+
+ public void testAddingSubscriberToPublishersAndGetListeneresToDifferentPublisher()
+ throws Exception {
+ // Add a subscription to a layer.
+ MockVmsSubscriber subscriber = new MockVmsSubscriber();
+
+ mRouting.addSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_2);
+ mRouting.addSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_1, PUBLISHER_ID_1);
+
+ Set<IVmsSubscriberClient> subscribers;
+ // Need to route a layer 1 message from publisher 2 so there are no subscribers.
+ subscribers =
+ mRouting.getSubscribersForLayerFromPublisher(
+ LAYER_WITH_SUBSCRIPTION_1,
+ PUBLISHER_ID_2);
+ assertEquals(0, subscribers.size());
+
+ // Need to route a layer 1 message from publisher 1 so there is one subscriber.
+ subscribers =
+ mRouting.getSubscribersForLayerFromPublisher(
+ LAYER_WITH_SUBSCRIPTION_1,
+ PUBLISHER_ID_1);
+ assertEquals(1, subscribers.size());
+ assertTrue(subscribers.contains(subscriber));
+
+ // Verify all the messages for LAYER_WITH_SUBSCRIPTION_2 have subscribers since the
+ // subscription was done without specifying a specific publisher.
+ subscribers =
+ mRouting.getSubscribersForLayerFromPublisher(
+ LAYER_WITH_SUBSCRIPTION_2,
+ PUBLISHER_ID_UNLISTED);
+ assertEquals(1, subscribers.size());
+ assertTrue(subscribers.contains(subscriber));
+ }
+
+
+ public void testRemovalOfSubscribersToPublishers() throws Exception {
+ // Add a subscription to a layer.
+ MockVmsSubscriber subscriber = new MockVmsSubscriber();
+ mRouting.addSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_1);
+
+ // Add a HAL subscription.
+ mRouting.addHalSubscription(LAYER_WITH_SUBSCRIPTION_2);
+
+ mRouting.addSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_1, PUBLISHER_ID_1);
+ mRouting.addSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_1, PUBLISHER_ID_2);
+ mRouting.addSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_2, PUBLISHER_ID_2);
+ mRouting.removeSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_2, PUBLISHER_ID_2);
+
+ // Verify expected subscriptions are in routing manager.
+ Set<VmsLayer> expectedSubscriptions = new HashSet<>();
+ expectedSubscriptions.add(LAYER_WITH_SUBSCRIPTION_1);
+ expectedSubscriptions.add(LAYER_WITH_SUBSCRIPTION_2);
+
+
+ Set<VmsAssociatedLayer> expectedSubscriptionsToPublishers = new HashSet<>();
+ expectedSubscriptionsToPublishers.add(new VmsAssociatedLayer(LAYER_WITH_SUBSCRIPTION_1,
+ new HashSet(Arrays.asList(PUBLISHER_ID_1, PUBLISHER_ID_2))));
+
+ VmsSubscriptionState subscriptionState = mRouting.getSubscriptionState();
+ assertEquals(6, subscriptionState.getSequenceNumber());
+ assertEquals(expectedSubscriptions,
+ new HashSet<>(subscriptionState.getLayers()));
+
+ assertEquals(expectedSubscriptionsToPublishers,
+ subscriptionState.getAssociatedLayers());
+
+ // Verify there is only a single subscriber.
+ assertEquals(1,
+ mRouting.getSubscribersForLayerFromPublisher(
+ LAYER_WITH_SUBSCRIPTION_1, PUBLISHER_ID_1).size());
+ }
+
+ public void testRemovalOfSubscribersToPublishersClearListForPublisher() throws Exception {
+ // Add a subscription to a layer.
+ MockVmsSubscriber subscriber = new MockVmsSubscriber();
+ mRouting.addSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_1);
+
+ // Add a HAL subscription.
+ mRouting.addHalSubscription(LAYER_WITH_SUBSCRIPTION_2);
+
+ mRouting.addSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_1, PUBLISHER_ID_1);
+ mRouting.addSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_1, PUBLISHER_ID_2);
+ mRouting.addSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_2, PUBLISHER_ID_2);
+ mRouting.removeSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_1, PUBLISHER_ID_1);
+
+ // Verify expected subscriptions are in routing manager.
+ Set<VmsLayer> expectedSubscriptions = new HashSet<>();
+ expectedSubscriptions.add(LAYER_WITH_SUBSCRIPTION_1);
+ expectedSubscriptions.add(LAYER_WITH_SUBSCRIPTION_2);
+
+ Set<VmsAssociatedLayer> expectedSubscriptionsToPublishers = new HashSet<>();
+ expectedSubscriptionsToPublishers.add(new VmsAssociatedLayer(LAYER_WITH_SUBSCRIPTION_1,
+ new HashSet(Arrays.asList(PUBLISHER_ID_2))));
+ expectedSubscriptionsToPublishers.add(new VmsAssociatedLayer(LAYER_WITH_SUBSCRIPTION_2,
+ new HashSet(Arrays.asList(PUBLISHER_ID_2))));
+
+ VmsSubscriptionState subscriptionState = mRouting.getSubscriptionState();
+ assertEquals(6, subscriptionState.getSequenceNumber());
+ assertEquals(expectedSubscriptions,
+ new HashSet<>(subscriptionState.getLayers()));
+
+ assertEquals(expectedSubscriptionsToPublishers,
+ subscriptionState.getAssociatedLayers());
+
+ // Verify there is only a single subscriber.
+ assertEquals(1,
+ mRouting.getSubscribersForLayerFromPublisher(
+ LAYER_WITH_SUBSCRIPTION_1, PUBLISHER_ID_1).size());
+ }
+
+ class MockVmsSubscriber extends IVmsSubscriberClient.Stub {
@Override
- public void onVmsMessageReceived(VmsLayer layer, byte[] payload) {}
+ public void onVmsMessageReceived(VmsLayer layer, byte[] payload) {
+ }
@Override
- public void onLayersAvailabilityChange(List<VmsLayer> availableLayers) {}
+ public void onLayersAvailabilityChanged(List<VmsAssociatedLayer> availableLayers) {
+ }
}
}
\ No newline at end of file
diff --git a/tests/obd2_test/src/com/android/car/obd2/test/Obd2FreezeFrameGeneratorTest.java b/tests/obd2_test/src/com/android/car/obd2/test/Obd2FreezeFrameGeneratorTest.java
index 20919a1..ee79618 100644
--- a/tests/obd2_test/src/com/android/car/obd2/test/Obd2FreezeFrameGeneratorTest.java
+++ b/tests/obd2_test/src/com/android/car/obd2/test/Obd2FreezeFrameGeneratorTest.java
@@ -16,7 +16,7 @@
package com.android.car.obd2.test;
-import static android.hardware.automotive.vehicle.V2_1.VehicleProperty.OBD2_FREEZE_FRAME;
+import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.OBD2_FREEZE_FRAME;
import static com.android.car.obd2.test.Utils.concatIntArrays;
import static com.android.car.obd2.test.Utils.stringsToIntArray;
import static org.junit.Assert.*;
diff --git a/tests/obd2_test/src/com/android/car/obd2/test/Obd2LiveFrameGeneratorTest.java b/tests/obd2_test/src/com/android/car/obd2/test/Obd2LiveFrameGeneratorTest.java
index ba3dbb8..a72e777 100644
--- a/tests/obd2_test/src/com/android/car/obd2/test/Obd2LiveFrameGeneratorTest.java
+++ b/tests/obd2_test/src/com/android/car/obd2/test/Obd2LiveFrameGeneratorTest.java
@@ -16,7 +16,7 @@
package com.android.car.obd2.test;
-import static android.hardware.automotive.vehicle.V2_1.VehicleProperty.OBD2_LIVE_FRAME;
+import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.OBD2_LIVE_FRAME;
import static com.android.car.obd2.test.Utils.concatIntArrays;
import static com.android.car.obd2.test.Utils.stringsToIntArray;
import static org.junit.Assert.*;
diff --git a/tests/vehiclehal_test/Android.mk b/tests/vehiclehal_test/Android.mk
index a22ee1b..cbc6ff1 100644
--- a/tests/vehiclehal_test/Android.mk
+++ b/tests/vehiclehal_test/Android.mk
@@ -34,8 +34,7 @@
LOCAL_STATIC_JAVA_LIBRARIES += vehicle-hal-support-lib \
android-support-test \
android.hidl.base-V1.0-java \
- android.hardware.automotive.vehicle-V2.0-java \
- android.hardware.automotive.vehicle-V2.1-java
+ android.hardware.automotive.vehicle-V2.0-java
LOCAL_JAVA_LIBRARIES := android.car android.test.runner
diff --git a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Obd2FreezeFrameTest.java b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Obd2FreezeFrameTest.java
index 23b9dbe..0d6048d 100644
--- a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Obd2FreezeFrameTest.java
+++ b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Obd2FreezeFrameTest.java
@@ -25,7 +25,7 @@
import android.hardware.automotive.vehicle.V2_0.IVehicle;
import android.hardware.automotive.vehicle.V2_0.StatusCode;
import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
-import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
import android.os.RemoteException;
import android.util.Log;
diff --git a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Obd2LiveFrameTest.java b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Obd2LiveFrameTest.java
index 8e14db3..25f2454 100644
--- a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Obd2LiveFrameTest.java
+++ b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Obd2LiveFrameTest.java
@@ -25,7 +25,7 @@
import android.hardware.automotive.vehicle.V2_0.IVehicle;
import android.hardware.automotive.vehicle.V2_0.StatusCode;
import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
-import android.hardware.automotive.vehicle.V2_1.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
import android.os.RemoteException;
import android.util.Log;
diff --git a/tools/bootanalyze/bootanalyze.py b/tools/bootanalyze/bootanalyze.py
index 480c9d4..b878358 100755
--- a/tools/bootanalyze/bootanalyze.py
+++ b/tools/bootanalyze/bootanalyze.py
@@ -30,6 +30,7 @@
import subprocess
import sys
import time
+import threading
import yaml
from datetime import datetime, date
@@ -92,10 +93,12 @@
if args.iterate > 1 and args.bootchart:
run_adb_shell_cmd_as_root('touch /data/bootchart/enabled')
- search_events = {key: re.compile(pattern)
+ search_events_pattern = {key: re.compile(pattern)
for key, pattern in cfg['events'].iteritems()}
- timing_events = {key: re.compile(pattern)
+ timing_events_pattern = {key: re.compile(pattern)
for key, pattern in cfg['timings'].iteritems()}
+ shutdown_events_pattern = {key: re.compile(pattern)
+ for key, pattern in cfg['shutdown_events'].iteritems()}
data_points = {}
kernel_timing_points = collections.OrderedDict()
@@ -103,6 +106,8 @@
boottime_points = collections.OrderedDict()
boot_chart_file_name_prefix = "bootchart-" + datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
systrace_file_name_prefix = "systrace-" + datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
+ shutdown_event_all = collections.OrderedDict()
+ shutdown_timing_event_all = collections.OrderedDict()
for it in range(0, args.iterate):
if args.iterate > 1:
print "Run: {0}".format(it)
@@ -112,9 +117,24 @@
boottime_events = None
while attempt <= MAX_RETRIES and processing_data is None:
attempt += 1
- processing_data, kernel_timings, logcat_timings, boottime_events = iterate(
- args, search_events, timing_events, cfg, error_time, components_to_monitor)
-
+ processing_data, kernel_timings, logcat_timings, boottime_events, shutdown_events,\
+ shutdown_timing_events = iterate(\
+ args, search_events_pattern, timing_events_pattern, shutdown_events_pattern, cfg,\
+ error_time, components_to_monitor)
+ if shutdown_events:
+ for k, v in shutdown_events.iteritems():
+ events = shutdown_event_all.get(k)
+ if not events:
+ events = []
+ shutdown_event_all[k] = events
+ events.append(v)
+ if shutdown_timing_events:
+ for k, v in shutdown_timing_events.iteritems():
+ events = shutdown_timing_event_all.get(k)
+ if not events:
+ events = []
+ shutdown_timing_event_all[k] = events
+ events.append(v)
if not processing_data or not boottime_events:
# Processing error
print "Failed to collect valid samples for run {0}".format(it)
@@ -151,6 +171,24 @@
if args.iterate > 1:
print "-----------------"
+ print "\nshutdown events after {0} runs".format(args.iterate)
+ print '{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs")
+ for item in shutdown_event_all.items():
+ num_runs = len(item[1])
+ print '{0:30}: {1:<7.5} {2:<7.5} {3} {4}'.format(
+ item[0], sum(item[1])/num_runs, stddev(item[1]),\
+ "*time taken" if item[0].startswith("init.") else "",\
+ num_runs if num_runs != args.iterate else "")
+ print "\nshutdown timing events after {0} runs".format(args.iterate)
+ print '{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs")
+ for item in shutdown_timing_event_all.items():
+ num_runs = len(item[1])
+ print '{0:30}: {1:<7.5} {2:<7.5} {3} {4}'.format(
+ item[0], sum(item[1])/num_runs, stddev(item[1]),\
+ "*time taken" if item[0].startswith("init.") else "",\
+ num_runs if num_runs != args.iterate else "")
+
+ print "-----------------"
print "ro.boottime.* after {0} runs".format(args.iterate)
print '{0:30}: {1:<7} {2:<7} {3}'.format("Event", "Mean", "stddev", "#runs")
for item in boottime_points.items():
@@ -243,19 +281,41 @@
item[0], item[1])
print "-----------------"
-def iterate(args, search_events, timings, cfg, error_time, components_to_monitor):
- if args.reboot:
- reboot(args.serial, args.stressfs != '', args.permissive, args.adb_reboot)
+def handle_reboot_log(capture_log_on_error, shutdown_events_pattern, components_to_monitor):
+ shutdown_events, shutdown_timing_events = collect_logcat_for_shutdown(capture_log_on_error,\
+ shutdown_events_pattern, components_to_monitor)
+ print "\nshutdown events: time"
+ for item in shutdown_events.items():
+ print '{0:30}: {1:<7.5}'.format(item[0], item[1])
+ print "\nshutdown timing events: time"
+ for item in shutdown_timing_events.items():
+ print '{0:30}: {1:<7.5}'.format(item[0], item[1])
+ return shutdown_events, shutdown_timing_events
- dmesg_events, kernel_timing_events = collect_events(search_events, ADB_CMD +\
- ' shell su root dmesg -w', timings,\
+def iterate(args, search_events_pattern, timings_pattern, shutdown_events_pattern, cfg, error_time,\
+ components_to_monitor):
+ shutdown_events = None
+ shutdown_timing_events = None
+ if args.reboot:
+ # sleep to make sure that logcat reader is reading before adb is gone by reboot. ugly but make
+ # impl simple.
+ t = threading.Thread(target = lambda : (time.sleep(2), reboot(args.serial, args.stressfs != '',\
+ args.permissive, args.adb_reboot)))
+ t.start()
+ shutdown_events, shutdown_timing_events = handle_reboot_log(True, shutdown_events_pattern,\
+ components_to_monitor)
+ t.join()
+
+ dmesg_events, kernel_timing_events = collect_events(search_events_pattern, ADB_CMD +\
+ ' shell su root dmesg -w', timings_pattern,\
[ KERNEL_BOOT_COMPLETE ], True)
logcat_stop_events = [ LOGCAT_BOOT_COMPLETE, KERNEL_BOOT_COMPLETE, LAUNCHER_START]
if args.fs_check:
logcat_stop_events.append("FsStat")
logcat_events, logcat_timing_events = collect_events(
- search_events, ADB_CMD + ' logcat -b all -v epoch', timings, logcat_stop_events, False)
+ search_events_pattern, ADB_CMD + ' logcat -b all -v epoch', timings_pattern,\
+ logcat_stop_events, False)
logcat_event_time = extract_time(
logcat_events, TIME_LOGCAT, float);
logcat_original_time = extract_time(
@@ -338,8 +398,8 @@
print "-----------------"
if args.timings:
- kernel_timing_points = generate_timing_points(kernel_timing_events, timings)
- logcat_timing_points = generate_timing_points(logcat_timing_events, timings)
+ kernel_timing_points = generate_timing_points(kernel_timing_events, timings_pattern)
+ logcat_timing_points = generate_timing_points(logcat_timing_events, timings_pattern)
dump_timing_points("Kernel", kernel_timing_points)
dump_timing_points("Logcat", logcat_timing_points)
@@ -401,7 +461,8 @@
if (fs_stat_val & ~0x17) != 0:
capture_bugreport("fs_stat_" + fs_stat, events[LOGCAT_BOOT_COMPLETE])
- return data_points, kernel_timing_points, logcat_timing_points, boottime_events
+ return data_points, kernel_timing_points, logcat_timing_points, boottime_events, shutdown_events,\
+ shutdown_timing_events
def debug(string):
if DEBUG:
@@ -503,6 +564,64 @@
existing_event = events.get(new_name)
return new_name
+def collect_logcat_for_shutdown(capture_log_on_error, shutdown_events_pattern,\
+ log_capture_conditions):
+ events = collections.OrderedDict()
+ # shutdown does not have timing_events but calculated from checking Xyz - XyzDone / XyzTimeout
+ timing_events = collections.OrderedDict()
+ process = subprocess.Popen(ADB_CMD + ' logcat -b all -v epoch', shell=True,
+ stdout=subprocess.PIPE);
+ lines = []
+ capture_log = False
+ shutdown_start_time = 0
+ while (True):
+ line = process.stdout.readline().lstrip().rstrip()
+ if not line:
+ break
+ lines.append(line)
+ event = get_boot_event(line, shutdown_events_pattern);
+ if not event:
+ continue
+ time = extract_a_time(line, TIME_LOGCAT, float)
+ if not time:
+ print "cannot get time from: " + line
+ continue
+ if shutdown_start_time == 0:
+ shutdown_start_time = time
+ time = time - shutdown_start_time
+ events[event] = time
+ time_limit1 = log_capture_conditions.get(event)
+ if time_limit1 and time_limit1 <= time:
+ capture_log = True
+ pair_event = None
+ if event.endswith('Done'):
+ pair_event = event[:-4]
+ elif event.endswith('Timeout'):
+ pair_event = event[:-7]
+ if capture_log_on_error:
+ capture_log = True
+ if not pair_event:
+ continue
+ start_time = events.get(pair_event)
+ if not start_time:
+ print "No start event for " + event
+ continue
+ time_spent = time - start_time
+ timing_event_name = pair_event + "Duration"
+ timing_events[timing_event_name] = time_spent
+ time_limit2 = log_capture_conditions.get(timing_event_name)
+ if time_limit2 and time_limit2 <= time_spent:
+ capture_log = True
+
+ if capture_log:
+ now = datetime.now()
+ log_file = ("shutdownlog-error-%s.txt") % (now.strftime("%Y-%m-%d-%H-%M-%S"))
+ print "Shutdown error, capture log to %s" % (log_file)
+ with open(log_file, 'w') as f:
+ f.write('\n'.join(lines))
+ return events, timing_events
+
+
def collect_events(search_events, command, timings, stop_events, disable_timing_after_zygote):
events = collections.OrderedDict()
timing_events = {}
diff --git a/tools/bootanalyze/bugreport_anayze.py b/tools/bootanalyze/bugreport_anayze.py
new file mode 100644
index 0000000..2575ebf
--- /dev/null
+++ b/tools/bootanalyze/bugreport_anayze.py
@@ -0,0 +1,386 @@
+#!/usr/bin/python
+
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Tool to analyze boot-up time from bugreport."""
+
+import argparse
+import collections
+import datetime
+import math
+import operator
+import os
+import re
+import select
+import subprocess
+import sys
+import time
+import threading
+import yaml
+
+from datetime import datetime, date
+
+DBG = True
+
+LOG_START_PATTERN = r"""\-\-\-\-\-\-\s(.*)\s\-\-\-\-\-\-"""
+LOG_END_PATTERN = r"""\-\-\-\-\-\-\s\S.*\s\-\-\-\-\-\-"""
+
+KERNEL_LOG_TITLE = "KERNEL LOG"
+SYSYEM_LOG_TITLE = "SYSTEM LOG"
+LAST_KMSG_TITLE = "LAST KMSG"
+LAST_LOGCAT_TITLE = "LAST LOGCAT"
+
+SYSTEM_PROPS_TITLE = "SYSTEM PROPERTIES"
+
+TIME_DMESG = "\[\s*(\d+\.\d+)\]"
+TIME_LOGCAT = "(\d+)\-(\d+)\s(\d+):(\d+):(\d+\.\d+)"
+
+NATIVE_CRASH_START_PATTERN = "I\sDEBUG\s+:\s\*\*\*\s\*\*\*"
+NATIVE_CRASH_PATTERN = "I\sDEBUG\s+:"
+JAVA_CRASH_START_PATTERN = "E\sAndroidRuntime:\sFATAL\sEXCEPTION"
+JAVA_CRASH_PATTERN = "E\sAndroidRuntime:\s"
+
+EPOCH = datetime.utcfromtimestamp(0)
+
+def init_arguments():
+ parser = argparse.ArgumentParser(description='Measures boot time from bugreport.')
+ parser.add_argument('-c', '--config', dest='config',
+ default='config.yaml', type=argparse.FileType('r'),
+ help='config file for the tool')
+ parser.add_argument('bugreport_file', nargs=1, help='bugreport txt file',
+ type=argparse.FileType('r'))
+ parser.add_argument('-n', '--iterate', dest='iterate', type=int, default=1,
+ help='number of time to repeat the measurement', )
+ return parser.parse_args()
+
+# Event per each reboot, for distinghishing current boot from last boot
+class Events:
+ def __init__(self):
+ self.events = collections.OrderedDict() #K: keyword, V:time in ms
+ self.timings = collections.OrderedDict()
+ self.shutdown_events = collections.OrderedDict()
+ self.java_crash = collections.OrderedDict() #K:time, V:list of crash infos, each entry per line
+ self.native_crash = collections.OrderedDict()
+
+ def reset_events_time(self, delta):
+ new_events = collections.OrderedDict()
+ for entry in self.events.iteritems():
+ new_events[entry[0]] = entry[1] - delta
+ self.events = new_events
+ if len(self.native_crash) > 0:
+ new_crash = collections.OrderedDict()
+ for entry in self.native_crash.iteritems():
+ new_crash[entry[0] - delta] = entry[1]
+ self.native_crash = new_crash
+ if len(self.java_crash) > 0:
+ new_crash = collections.OrderedDict()
+ for entry in self.java_crash.iteritems():
+ new_crash[entry[0] - delta] = entry[1]
+ self.java_crash = new_crash
+
+ def reset_shutdown_events_time(self):
+ if len(self.shutdown_events) == 0:
+ return
+ time_offset = 0
+ new_events = collections.OrderedDict()
+ for entry in self.shutdown_events.iteritems():
+ if time_offset == 0:
+ time_offset = entry[1]
+ new_events[entry[0]] = entry[1] - time_offset
+ self.shutdown_events = new_events
+
+ def dump_dict(self, d):
+ for entry in d.iteritems():
+ print ' {0:30}: {1}'.format(entry[0], entry[1])
+
+ def dump_crash(self, time, stack):
+ print " Crash time:", time, " stack:"
+ print ' '.join(stack)
+
+ def dump(self):
+ if len(self.events) > 0:
+ print "\n***Events:"
+ self.dump_dict(self.events)
+ if len(self.timings) > 0:
+ print "\n***Timings top 20"
+ timings_sorted = sorted(self.timings.items(), key = lambda item: item[1], reverse=True)
+ nums_to_dump = min(20, len(timings_sorted))
+ for i in range(nums_to_dump):
+ print ' {0:30}: {1}'.format(timings_sorted[i][0], timings_sorted[i][1])
+ print "\n***Timings:"
+ self.dump_dict(self.timings)
+ if len(self.shutdown_events) > 0:
+ print "\n***Shutdown Events (time relative to the begining of shutdown) :"
+ self.dump_dict(self.shutdown_events)
+ if len(self.native_crash) > 0:
+ print "\n***Native crash founds:", len(self.native_crash)
+ for entry in self.native_crash.iteritems():
+ self.dump_crash(entry[0], entry[1])
+ if len(self.java_crash) > 0:
+ print "\n***Java crash founds:", len(self.java_crash)
+ for entry in self.java_crash.iteritems():
+ self.dump_crash(entry[0], entry[1])
+
+class Parser:
+ def __init__(self, config_file, bugreport_file):
+ self.re_log_start = re.compile(LOG_START_PATTERN)
+ self.re_log_end = re.compile(LOG_END_PATTERN)
+ self.f = bugreport_file
+ cfg = yaml.load(config_file)
+ self.event_patterns = {key: re.compile(pattern)
+ for key, pattern in cfg['events'].iteritems()}
+ self.timing_patterns = {key: re.compile(pattern)
+ for key, pattern in cfg['timings'].iteritems()}
+ self.shutdown_event_patterns = {key: re.compile(pattern)
+ for key, pattern in cfg['shutdown_events'].iteritems()}
+ self.current_boot_kernel = Events()
+ self.current_boot_logcat = Events()
+ self.last_boot_kernel = Events()
+ self.last_boot_logcat = Events()
+ self.boottime_props = collections.OrderedDict() # K:prop, V:boot time, added in boot time order
+ self.bootloader_time = 0
+ self.re_time_dmesg = re.compile(TIME_DMESG)
+ self.re_time_logcat = re.compile(TIME_LOGCAT)
+ self.re_native_crash_start = re.compile(NATIVE_CRASH_START_PATTERN)
+ self.re_native_crash = re.compile(NATIVE_CRASH_PATTERN)
+ self.re_java_crash_start = re.compile(JAVA_CRASH_START_PATTERN)
+ self.re_java_crash = re.compile(JAVA_CRASH_PATTERN)
+
+ def match_an_event(self, event_patterns, line):
+ for event_key, event_pattern in event_patterns.iteritems():
+ m = event_pattern.search(line)
+ if m:
+ return event_key, m
+ return None, None
+
+ def get_event_time(self, line, is_kernel):
+ if is_kernel:
+ m = self.re_time_dmesg.search(line)
+ if not m:
+ print "Cannot get time from log:", line
+ return -1
+ return int(float(m.group(1)) * 1000)
+ else:
+ m = self.re_time_logcat.search(line)
+ if not m:
+ print "Cannot get time from log:", line
+ return -1
+ mm = int(m.group(1))
+ dd = int(m.group(2))
+ hh = int(m.group(3))
+ min = int(m.group(4))
+ usecs = int(float(m.group(5)) * 1000000)
+ secs = usecs / 1000000
+ usecs = usecs - 1000000 * secs
+ dt = datetime(2017, mm, dd, hh, min, secs, usecs)
+ return int((dt - EPOCH).total_seconds() * 1000)
+
+ def queue_crash(self, event, crash_time, crash_stacks, is_native_crash):
+ stacks = list(crash_stacks)
+ if is_native_crash:
+ event.native_crash[crash_time] = stacks
+ else:
+ event.java_crash[crash_time] = stacks
+
+ def check_crash(self, event, orig_line):
+ line = orig_line
+ crash_time = 0
+ crash_stacks = []
+ is_native_crash = True
+ while len(line) > 0:
+ m = self.re_native_crash_start.search(line)
+ if m:
+ if len(crash_stacks) > 0:
+ self.queue_crash(event, crash_time, crash_stacks, is_native_crash)
+ crash_stacks = []
+ is_native_crash = True
+ crash_stacks.append(line)
+ crash_time = self.get_event_time(line, False)
+ line = self.f.readline()
+ continue
+ m = self.re_native_crash.search(line)
+ if m:
+ crash_stacks.append(line)
+ line = self.f.readline()
+ continue
+ m = self.re_java_crash_start.search(line)
+ if m:
+ if len(crash_stacks) > 0:
+ self.queue_crash(event, crash_time, crash_stacks, is_native_crash)
+ crash_stacks = []
+ is_native_crash = False
+ crash_stacks.append(line)
+ crash_time = self.get_event_time(line, False)
+ line = self.f.readline()
+ continue
+ m = self.re_java_crash.search(line)
+ if m:
+ crash_stacks.append(line)
+ line = self.f.readline()
+ continue
+ # reaching here means not crash, so return new line
+ if line != orig_line:
+ return line
+ else:
+ return self.f.readline()
+
+
+
+ def handle_events(self, event, is_kernel):
+ line = self.f.readline()
+ while len(line) > 0 and not self.re_log_end.match(line):
+ key, m = self.match_an_event(self.event_patterns, line)
+ if m:
+ event.events[key] = self.get_event_time(line, is_kernel)
+ line = self.f.readline()
+ continue
+ key, m = self.match_an_event(self.timing_patterns, line)
+ if m:
+ name = m.group('name')
+ time = float(m.group('time'))
+ if key.endswith('_secs'):
+ time = time * 1000
+ event.timings[name] = int(time)
+ line = self.f.readline()
+ continue
+ key, m = self.match_an_event(self.shutdown_event_patterns, line)
+ if m:
+ event.shutdown_events[key] = self.get_event_time(line, is_kernel)
+ line = self.f.readline()
+ continue
+ if not is_kernel: # collect crash
+ line = self.check_crash(event, line)
+ continue
+ line = self.f.readline()
+
+ def handle_kernel_log(self):
+ if DBG:
+ print "start " + KERNEL_LOG_TITLE
+ self.handle_events(self.current_boot_kernel, True)
+
+ def handle_system_log(self):
+ if DBG:
+ print "start " + SYSYEM_LOG_TITLE
+ self.handle_events(self.current_boot_logcat, False)
+
+ def handle_last_kernel_log(self):
+ if DBG:
+ print "start " + LAST_KMSG_TITLE
+ self.handle_events(self.last_boot_kernel, True)
+
+ def handle_last_system_log(self):
+ if DBG:
+ print "start " + LAST_LOGCAT_TITLE
+ self.handle_events(self.last_boot_logcat, False)
+
+ def handle_system_props(self):
+ if DBG:
+ print "start " + SYSTEM_PROPS_TITLE
+ re_prop = re.compile(r"""\[(.+)\].*\[(.*)\]""")
+ RO_BOOTTIME_PROP = "ro.boottime."
+ boottime_props = {} # K: prop name, V: boot time in ms
+ line = self.f.readline()
+ while len(line) > 0 and not self.re_log_end.match(line):
+ m = re_prop.match(line)
+ if not m:
+ print "Cannot parse prop:", line
+ line = self.f.readline()
+ continue
+ if m.group(1).startswith(RO_BOOTTIME_PROP):
+ name = m.group(1)[len(RO_BOOTTIME_PROP):]
+ time = int(m.group(2)) / 1000000 # ns to ms
+ boottime_props[name] = time
+ elif m.group(1) == "ro.boot.boottime":
+ print "Found bootloader boottime ", line
+ entries = m.group(2).split(",")
+ for entry in entries:
+ values = entry.split(":")
+ if values[0] != "SW":
+ self.bootloader_time += int(values[1])
+ line = self.f.readline()
+ self.boottime_props = collections.OrderedDict(sorted(
+ boottime_props.items(), key = lambda item: item[1]))
+
+ def parse(self):
+ while (True):
+ l = self.f.readline()
+ if len(l) == 0: # EOF
+ return
+ m = self.re_log_start.match(l)
+ if not m:
+ continue
+ #print m.group(1)
+ if m.group(1).startswith(KERNEL_LOG_TITLE):
+ self.handle_kernel_log()
+ elif m.group(1).startswith(SYSYEM_LOG_TITLE):
+ self.handle_system_log()
+ elif m.group(1).startswith(SYSTEM_PROPS_TITLE):
+ self.handle_system_props()
+ elif m.group(1).startswith(LAST_KMSG_TITLE):
+ self.handle_last_kernel_log()
+ elif m.group(1).startswith(LAST_LOGCAT_TITLE):
+ self.handle_last_system_log()
+
+ def dump_props(self):
+ if self.bootloader_time != 0:
+ print "*bootloader time:", self.bootloader_time
+ if self.boottime_props:
+ print "*ro.boottime.*:"
+ for name, t in self.boottime_props.iteritems():
+ print ' {0:30}: {1}'.format(name, t)
+
+ def reset_event_times(self, kernel_event, logcat_event):
+ has_boot_complete = True
+ kernel_bootcomplete_time = kernel_event.events.get("BootComplete_kernel")
+ if not kernel_bootcomplete_time:
+ has_boot_complete = False
+ logcat_bootcomplete_time = logcat_event.events.get("BootComplete")
+ if not logcat_bootcomplete_time:
+ has_boot_complete = False
+ time_delta = 0
+ if has_boot_complete:
+ time_delta = logcat_bootcomplete_time - kernel_bootcomplete_time
+ else:
+ time_delta = logcat_event.events.values()[0] if len(logcat_event.events) > 0 else 0
+ logcat_event.reset_events_time(time_delta)
+ logcat_event.reset_shutdown_events_time()
+ kernel_event.reset_shutdown_events_time()
+ return has_boot_complete
+
+ def dump(self):
+ self.dump_props()
+ boot_complete_found = self.reset_event_times(self.current_boot_kernel, self.current_boot_logcat)
+ print "* Kernel dmesg:"
+ self.current_boot_kernel.dump()
+ print "\n\n* Logcat " + ("(time matched with kernel dmesg):" if boot_complete_found\
+ else "(time set relative to the first event):")
+ self.current_boot_logcat.dump()
+ print "\n\n\n==== Data from last boot ==="
+ boot_complete_found = self.reset_event_times(self.last_boot_kernel, self.last_boot_logcat)
+ print "\n\n* Last Kernel dmesg:"
+ self.last_boot_kernel.dump()
+ print "\n\n* Last Logcat " + ("(time matched with kernel dmesg):" if boot_complete_found \
+ else "(time set relative to the first event):")
+ self.last_boot_logcat.dump()
+
+def main():
+ args = init_arguments()
+
+ parser = Parser(args.config, args.bugreport_file[0])
+ parser.parse()
+ parser.dump()
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/bootanalyze/config.yaml b/tools/bootanalyze/config.yaml
index ea60434..37f2891 100644
--- a/tools/bootanalyze/config.yaml
+++ b/tools/bootanalyze/config.yaml
@@ -1,13 +1,13 @@
#YAML
time_correction_key: correction
timings:
- system_server: SystemServerTiming(Async)?\s*:\s*(?P<name>[^\s]+) took to complete:\s(?P<time>[0-9]+)ms
+ system_server: SystemServerTiming(Async)?\s*:\s*(?P<name>\S+) took to complete:\s(?P<time>[0-9]+)ms
fs_shutdown: (?P<name>boot_fs_shutdown),(?P<time>[0-9]+),([0-9]+)
- ueventd_secs: ueventd:\s(?P<name>[^\s].+)\stook\s(?P<time>[.0-9]+)\sseconds
- init_command_ms: init:\sCommand\s(?P<name>[^\s].+)\sreturned.*took\s(?P<time>[.0-9]+)ms
- init_service_exec_secs: init:\sService\s.*exec\s[^\s]+\s\((?P<name>[^\s].+)\).*pid.*\swaiting\stook\s(?P<time>[.0-9]+)\sseconds
- zygote64_timing: (?P<name>Zygote64Timing\:\s[^\s]+)\stook\sto\scomplete\:\s(?P<time>[0-9]+)ms
- zygote32_timing: (?P<name>Zygote32Timing\:\s[^\s]+)\stook\sto\scomplete\:\s(?P<time>[0-9]+)ms
+ ueventd_secs: ueventd:\s(?P<name>\S.+)\stook\s(?P<time>[.0-9]+)\sseconds
+ init_command_ms: init:\sCommand\s(?P<name>\S.+)\sreturned.*took\s(?P<time>[.0-9]+)ms
+ init_service_exec_secs: init:\sService\s.*exec\s\S+\s\((?P<name>\S.+)\).*pid.*\swaiting\stook\s(?P<time>[.0-9]+)\sseconds
+ zygote64_timing: (?P<name>Zygote64Timing\:\s\S+)\stook\sto\scomplete\:\s(?P<time>[0-9]+)ms
+ zygote32_timing: (?P<name>Zygote32Timing\:\s\S+)\stook\sto\scomplete\:\s(?P<time>[0-9]+)ms
events:
kernel: Linux version
android_init_1st_stage: init first stage started
@@ -40,7 +40,6 @@
SystemServer_start: Entered the Android system server!
system_server_ready: Enabled StrictMode for system server main
PackageManagerInit_start: SystemServer\s*:\s*StartPackageManagerService
- PackageManagerInit_ready: StartPackageManagerService took to complete
BluetoothService_start: Starting com.android.server.BluetoothService
SystemUi_start: for service com.android.systemui/.
CarLauncherReady: Em.Overview:\s*onResume
@@ -57,4 +56,23 @@
BootComplete_kernel: processing action \(sys\.boot_completed=1\)
LauncherStart: START.*HOME.*(NexusLauncherActivity|GEL|LensPickerTrampolineActivity|SetupWizardActivity)
FsStat: fs_stat, partition:userdata stat:(0x\S+)
-
+shutdown_events:
+ ShutdownStart: ShutdownThread:\sNotifying thread to start shutdown
+ ShutdownBroadcast: ShutdownThread:\sSending shutdown broadcast
+ ShutdownActivityManagerService: ShutdownThread:\sShutting down activity manager
+ ShutdownPackageManagerService: ShutdownThread:\sShutting down package manager
+ ShutdownNfc: ShutdownThread:\sTurning off NFC
+ ShutdownBt: ShutdownThread:\sDisabling Bluetooth
+ ShutdownRadio: ShutdownThread:\sTurning off cellular radios
+ ShutdownRadiosWait: ShutdownThread:\sWaiting for NFC, Bluetooth and Radio
+ ShutdownBtDone: ShutdownThread:\sBluetooth turned off
+ ShutdownRadioDone: ShutdownThread:\sRadio turned off
+ ShutdownNfcDone: ShutdownThread:\sNFC turned off
+ ShutdownRadiosWaitDone: ShutdownThread:\sNFC, Radio and Bluetooth shutdown complete
+ ShutdownRadiosWaitTimeout: ShutdownThread:\sTimed out waiting for NFC, Radio and Bluetooth shutdown
+ ShutdownStorageManagerSerivce: ShutdownThread:\sShutting down StorageManagerService
+ ShutdownStorageManagerSerivceDone: ShutdownThread:\sResult code [\d]+ from StorageManagerService\.shutdown
+ ShutdownStorageManagerSerivceTimeout: ShutdownThread:\sShutdown wait timed out
+ ShutdownStartDone: ShutdownThread:\sPerforming low-level shutdown
+ ShutdownInitAction: init\s+:\sprocessing action \(sys\.shutdown\.requested
+ ShutdownInitFsShutdown: init\s+:\sShutdown timeout
diff --git a/tools/emulator/diagnostic_injector.py b/tools/emulator/diagnostic_injector.py
index 4bd3317..7678576 100755
--- a/tools/emulator/diagnostic_injector.py
+++ b/tools/emulator/diagnostic_injector.py
@@ -20,8 +20,9 @@
# Use thusly:
# $ ./diagnostic_injector.py <path/to/diagnostic.json>
-import sys
+import argparse
import json
+import sys
import time
import vhal_consts_2_1 as c
@@ -48,8 +49,8 @@
from diagnostic_builder import DiagnosticEventBuilder
class DiagnosticHalWrapper(object):
- def __init__(self):
- self.vhal = Vhal(c.vhal_types_2_0)
+ def __init__(self, device):
+ self.vhal = Vhal(c.vhal_types_2_0, device)
self.liveFrameConfig = self.chat(
lambda hal: hal.getConfig(c.VEHICLEPROPERTY_OBD2_LIVE_FRAME))
self.freezeFrameConfig = self.chat(
@@ -105,11 +106,14 @@
else:
print("fail: %s" % status)
-if len(sys.argv) < 2:
- print("Syntax: diagnostic_injector.py <path/to/diagnostic.json>")
- sys.exit(1)
+parser = argparse.ArgumentParser(description='Diagnostic Events Injector')
+parser.add_argument('jsondoc', nargs='+')
+parser.add_argument('-s', action='store', dest='deviceid', default=None)
-halWrapper = DiagnosticHalWrapper()
+args = parser.parse_args()
-for arg in sys.argv[1:]:
+halWrapper = DiagnosticHalWrapper(device=args.deviceid)
+
+for arg in args.jsondoc:
+ print("Injecting %s" % arg)
halWrapper.inject(arg)
diff --git a/tools/emulator/diagnostic_sensors.py b/tools/emulator/diagnostic_sensors.py
index abde4b8..c808182 100644
--- a/tools/emulator/diagnostic_sensors.py
+++ b/tools/emulator/diagnostic_sensors.py
@@ -17,115 +17,115 @@
# This file is generated by types.hal by packages/services/Car/tools/update-obd2-sensors.py
# DO NOT EDIT MANUALLY
-OBD2_SENSOR_INTEGER_FUEL_SYSTEM_STATUS = 0
-OBD2_SENSOR_INTEGER_MALFUNCTION_INDICATOR_LIGHT_ON = 1
-OBD2_SENSOR_INTEGER_IGNITION_MONITORS_SUPPORTED = 2
-OBD2_SENSOR_INTEGER_IGNITION_SPECIFIC_MONITORS = 3
-OBD2_SENSOR_INTEGER_INTAKE_AIR_TEMPERATURE = 4
-OBD2_SENSOR_INTEGER_COMMANDED_SECONDARY_AIR_STATUS = 5
-OBD2_SENSOR_INTEGER_NUM_OXYGEN_SENSORS_PRESENT = 6
-OBD2_SENSOR_INTEGER_RUNTIME_SINCE_ENGINE_START = 7
-OBD2_SENSOR_INTEGER_DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON = 8
-OBD2_SENSOR_INTEGER_WARMUPS_SINCE_CODES_CLEARED = 9
-OBD2_SENSOR_INTEGER_DISTANCE_TRAVELED_SINCE_CODES_CLEARED = 10
-OBD2_SENSOR_INTEGER_ABSOLUTE_BAROMETRIC_PRESSURE = 11
-OBD2_SENSOR_INTEGER_CONTROL_MODULE_VOLTAGE = 12
-OBD2_SENSOR_INTEGER_AMBIENT_AIR_TEMPERATURE = 13
-OBD2_SENSOR_INTEGER_TIME_WITH_MALFUNCTION_LIGHT_ON = 14
-OBD2_SENSOR_INTEGER_TIME_SINCE_TROUBLE_CODES_CLEARED = 15
-OBD2_SENSOR_INTEGER_MAX_FUEL_AIR_EQUIVALENCE_RATIO = 16
-OBD2_SENSOR_INTEGER_MAX_OXYGEN_SENSOR_VOLTAGE = 17
-OBD2_SENSOR_INTEGER_MAX_OXYGEN_SENSOR_CURRENT = 18
-OBD2_SENSOR_INTEGER_MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 19
-OBD2_SENSOR_INTEGER_MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR = 20
-OBD2_SENSOR_INTEGER_FUEL_TYPE = 21
-OBD2_SENSOR_INTEGER_FUEL_RAIL_ABSOLUTE_PRESSURE = 22
-OBD2_SENSOR_INTEGER_ENGINE_OIL_TEMPERATURE = 23
-OBD2_SENSOR_INTEGER_DRIVER_DEMAND_PERCENT_TORQUE = 24
-OBD2_SENSOR_INTEGER_ENGINE_ACTUAL_PERCENT_TORQUE = 25
-OBD2_SENSOR_INTEGER_ENGINE_REFERENCE_PERCENT_TORQUE = 26
-OBD2_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_IDLE = 27
-OBD2_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT1 = 28
-OBD2_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT2 = 29
-OBD2_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT3 = 30
-OBD2_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT4 = 31
-OBD2_SENSOR_INTEGER_LAST_SYSTEM_INDEX = OBD2_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT4
-OBD2_SENSOR_INTEGER_VENDOR_START_INDEX = OBD2_SENSOR_INTEGER_LAST_SYSTEM_INDEX + 1
+DIAGNOSTIC_SENSOR_INTEGER_FUEL_SYSTEM_STATUS = 0
+DIAGNOSTIC_SENSOR_INTEGER_MALFUNCTION_INDICATOR_LIGHT_ON = 1
+DIAGNOSTIC_SENSOR_INTEGER_IGNITION_MONITORS_SUPPORTED = 2
+DIAGNOSTIC_SENSOR_INTEGER_IGNITION_SPECIFIC_MONITORS = 3
+DIAGNOSTIC_SENSOR_INTEGER_INTAKE_AIR_TEMPERATURE = 4
+DIAGNOSTIC_SENSOR_INTEGER_COMMANDED_SECONDARY_AIR_STATUS = 5
+DIAGNOSTIC_SENSOR_INTEGER_NUM_OXYGEN_SENSORS_PRESENT = 6
+DIAGNOSTIC_SENSOR_INTEGER_RUNTIME_SINCE_ENGINE_START = 7
+DIAGNOSTIC_SENSOR_INTEGER_DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON = 8
+DIAGNOSTIC_SENSOR_INTEGER_WARMUPS_SINCE_CODES_CLEARED = 9
+DIAGNOSTIC_SENSOR_INTEGER_DISTANCE_TRAVELED_SINCE_CODES_CLEARED = 10
+DIAGNOSTIC_SENSOR_INTEGER_ABSOLUTE_BAROMETRIC_PRESSURE = 11
+DIAGNOSTIC_SENSOR_INTEGER_CONTROL_MODULE_VOLTAGE = 12
+DIAGNOSTIC_SENSOR_INTEGER_AMBIENT_AIR_TEMPERATURE = 13
+DIAGNOSTIC_SENSOR_INTEGER_TIME_WITH_MALFUNCTION_LIGHT_ON = 14
+DIAGNOSTIC_SENSOR_INTEGER_TIME_SINCE_TROUBLE_CODES_CLEARED = 15
+DIAGNOSTIC_SENSOR_INTEGER_MAX_FUEL_AIR_EQUIVALENCE_RATIO = 16
+DIAGNOSTIC_SENSOR_INTEGER_MAX_OXYGEN_SENSOR_VOLTAGE = 17
+DIAGNOSTIC_SENSOR_INTEGER_MAX_OXYGEN_SENSOR_CURRENT = 18
+DIAGNOSTIC_SENSOR_INTEGER_MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 19
+DIAGNOSTIC_SENSOR_INTEGER_MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR = 20
+DIAGNOSTIC_SENSOR_INTEGER_FUEL_TYPE = 21
+DIAGNOSTIC_SENSOR_INTEGER_FUEL_RAIL_ABSOLUTE_PRESSURE = 22
+DIAGNOSTIC_SENSOR_INTEGER_ENGINE_OIL_TEMPERATURE = 23
+DIAGNOSTIC_SENSOR_INTEGER_DRIVER_DEMAND_PERCENT_TORQUE = 24
+DIAGNOSTIC_SENSOR_INTEGER_ENGINE_ACTUAL_PERCENT_TORQUE = 25
+DIAGNOSTIC_SENSOR_INTEGER_ENGINE_REFERENCE_PERCENT_TORQUE = 26
+DIAGNOSTIC_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_IDLE = 27
+DIAGNOSTIC_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT1 = 28
+DIAGNOSTIC_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT2 = 29
+DIAGNOSTIC_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT3 = 30
+DIAGNOSTIC_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT4 = 31
+DIAGNOSTIC_SENSOR_INTEGER_LAST_SYSTEM_INDEX = DIAGNOSTIC_SENSOR_INTEGER_ENGINE_PERCENT_TORQUE_DATA_POINT4
+DIAGNOSTIC_SENSOR_INTEGER_VENDOR_START_INDEX = DIAGNOSTIC_SENSOR_INTEGER_LAST_SYSTEM_INDEX + 1
-OBD2_SENSOR_FLOAT_CALCULATED_ENGINE_LOAD = 0
-OBD2_SENSOR_FLOAT_ENGINE_COOLANT_TEMPERATURE = 1
-OBD2_SENSOR_FLOAT_SHORT_TERM_FUEL_TRIM_BANK1 = 2
-OBD2_SENSOR_FLOAT_LONG_TERM_FUEL_TRIM_BANK1 = 3
-OBD2_SENSOR_FLOAT_SHORT_TERM_FUEL_TRIM_BANK2 = 4
-OBD2_SENSOR_FLOAT_LONG_TERM_FUEL_TRIM_BANK2 = 5
-OBD2_SENSOR_FLOAT_FUEL_PRESSURE = 6
-OBD2_SENSOR_FLOAT_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 7
-OBD2_SENSOR_FLOAT_ENGINE_RPM = 8
-OBD2_SENSOR_FLOAT_VEHICLE_SPEED = 9
-OBD2_SENSOR_FLOAT_TIMING_ADVANCE = 10
-OBD2_SENSOR_FLOAT_MAF_AIR_FLOW_RATE = 11
-OBD2_SENSOR_FLOAT_THROTTLE_POSITION = 12
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR1_VOLTAGE = 13
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM = 14
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO = 15
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR2_VOLTAGE = 16
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM = 17
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO = 18
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR3_VOLTAGE = 19
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM = 20
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO = 21
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR4_VOLTAGE = 22
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM = 23
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO = 24
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR5_VOLTAGE = 25
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM = 26
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO = 27
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR6_VOLTAGE = 28
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM = 29
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO = 30
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR7_VOLTAGE = 31
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM = 32
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO = 33
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR8_VOLTAGE = 34
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM = 35
-OBD2_SENSOR_FLOAT_OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO = 36
-OBD2_SENSOR_FLOAT_FUEL_RAIL_PRESSURE = 37
-OBD2_SENSOR_FLOAT_FUEL_RAIL_GAUGE_PRESSURE = 38
-OBD2_SENSOR_FLOAT_COMMANDED_EXHAUST_GAS_RECIRCULATION = 39
-OBD2_SENSOR_FLOAT_EXHAUST_GAS_RECIRCULATION_ERROR = 40
-OBD2_SENSOR_FLOAT_COMMANDED_EVAPORATIVE_PURGE = 41
-OBD2_SENSOR_FLOAT_FUEL_TANK_LEVEL_INPUT = 42
-OBD2_SENSOR_FLOAT_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 43
-OBD2_SENSOR_FLOAT_CATALYST_TEMPERATURE_BANK1_SENSOR1 = 44
-OBD2_SENSOR_FLOAT_CATALYST_TEMPERATURE_BANK2_SENSOR1 = 45
-OBD2_SENSOR_FLOAT_CATALYST_TEMPERATURE_BANK1_SENSOR2 = 46
-OBD2_SENSOR_FLOAT_CATALYST_TEMPERATURE_BANK2_SENSOR2 = 47
-OBD2_SENSOR_FLOAT_ABSOLUTE_LOAD_VALUE = 48
-OBD2_SENSOR_FLOAT_FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO = 49
-OBD2_SENSOR_FLOAT_RELATIVE_THROTTLE_POSITION = 50
-OBD2_SENSOR_FLOAT_ABSOLUTE_THROTTLE_POSITION_B = 51
-OBD2_SENSOR_FLOAT_ABSOLUTE_THROTTLE_POSITION_C = 52
-OBD2_SENSOR_FLOAT_ACCELERATOR_PEDAL_POSITION_D = 53
-OBD2_SENSOR_FLOAT_ACCELERATOR_PEDAL_POSITION_E = 54
-OBD2_SENSOR_FLOAT_ACCELERATOR_PEDAL_POSITION_F = 55
-OBD2_SENSOR_FLOAT_COMMANDED_THROTTLE_ACTUATOR = 56
-OBD2_SENSOR_FLOAT_ETHANOL_FUEL_PERCENTAGE = 57
-OBD2_SENSOR_FLOAT_ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 58
-OBD2_SENSOR_FLOAT_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 59
-OBD2_SENSOR_FLOAT_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 60
-OBD2_SENSOR_FLOAT_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 61
-OBD2_SENSOR_FLOAT_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 62
-OBD2_SENSOR_FLOAT_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 63
-OBD2_SENSOR_FLOAT_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 64
-OBD2_SENSOR_FLOAT_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 65
-OBD2_SENSOR_FLOAT_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 66
-OBD2_SENSOR_FLOAT_RELATIVE_ACCELERATOR_PEDAL_POSITION = 67
-OBD2_SENSOR_FLOAT_HYBRID_BATTERY_PACK_REMAINING_LIFE = 68
-OBD2_SENSOR_FLOAT_FUEL_INJECTION_TIMING = 69
-OBD2_SENSOR_FLOAT_ENGINE_FUEL_RATE = 70
-OBD2_SENSOR_FLOAT_LAST_SYSTEM_INDEX = OBD2_SENSOR_FLOAT_ENGINE_FUEL_RATE
-OBD2_SENSOR_FLOAT_VENDOR_START_INDEX = OBD2_SENSOR_FLOAT_LAST_SYSTEM_INDEX + 1
+DIAGNOSTIC_SENSOR_FLOAT_CALCULATED_ENGINE_LOAD = 0
+DIAGNOSTIC_SENSOR_FLOAT_ENGINE_COOLANT_TEMPERATURE = 1
+DIAGNOSTIC_SENSOR_FLOAT_SHORT_TERM_FUEL_TRIM_BANK1 = 2
+DIAGNOSTIC_SENSOR_FLOAT_LONG_TERM_FUEL_TRIM_BANK1 = 3
+DIAGNOSTIC_SENSOR_FLOAT_SHORT_TERM_FUEL_TRIM_BANK2 = 4
+DIAGNOSTIC_SENSOR_FLOAT_LONG_TERM_FUEL_TRIM_BANK2 = 5
+DIAGNOSTIC_SENSOR_FLOAT_FUEL_PRESSURE = 6
+DIAGNOSTIC_SENSOR_FLOAT_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 7
+DIAGNOSTIC_SENSOR_FLOAT_ENGINE_RPM = 8
+DIAGNOSTIC_SENSOR_FLOAT_VEHICLE_SPEED = 9
+DIAGNOSTIC_SENSOR_FLOAT_TIMING_ADVANCE = 10
+DIAGNOSTIC_SENSOR_FLOAT_MAF_AIR_FLOW_RATE = 11
+DIAGNOSTIC_SENSOR_FLOAT_THROTTLE_POSITION = 12
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR1_VOLTAGE = 13
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM = 14
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO = 15
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR2_VOLTAGE = 16
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM = 17
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO = 18
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR3_VOLTAGE = 19
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM = 20
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO = 21
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR4_VOLTAGE = 22
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM = 23
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO = 24
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR5_VOLTAGE = 25
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM = 26
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO = 27
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR6_VOLTAGE = 28
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM = 29
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO = 30
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR7_VOLTAGE = 31
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM = 32
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO = 33
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR8_VOLTAGE = 34
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM = 35
+DIAGNOSTIC_SENSOR_FLOAT_OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO = 36
+DIAGNOSTIC_SENSOR_FLOAT_FUEL_RAIL_PRESSURE = 37
+DIAGNOSTIC_SENSOR_FLOAT_FUEL_RAIL_GAUGE_PRESSURE = 38
+DIAGNOSTIC_SENSOR_FLOAT_COMMANDED_EXHAUST_GAS_RECIRCULATION = 39
+DIAGNOSTIC_SENSOR_FLOAT_EXHAUST_GAS_RECIRCULATION_ERROR = 40
+DIAGNOSTIC_SENSOR_FLOAT_COMMANDED_EVAPORATIVE_PURGE = 41
+DIAGNOSTIC_SENSOR_FLOAT_FUEL_TANK_LEVEL_INPUT = 42
+DIAGNOSTIC_SENSOR_FLOAT_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 43
+DIAGNOSTIC_SENSOR_FLOAT_CATALYST_TEMPERATURE_BANK1_SENSOR1 = 44
+DIAGNOSTIC_SENSOR_FLOAT_CATALYST_TEMPERATURE_BANK2_SENSOR1 = 45
+DIAGNOSTIC_SENSOR_FLOAT_CATALYST_TEMPERATURE_BANK1_SENSOR2 = 46
+DIAGNOSTIC_SENSOR_FLOAT_CATALYST_TEMPERATURE_BANK2_SENSOR2 = 47
+DIAGNOSTIC_SENSOR_FLOAT_ABSOLUTE_LOAD_VALUE = 48
+DIAGNOSTIC_SENSOR_FLOAT_FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO = 49
+DIAGNOSTIC_SENSOR_FLOAT_RELATIVE_THROTTLE_POSITION = 50
+DIAGNOSTIC_SENSOR_FLOAT_ABSOLUTE_THROTTLE_POSITION_B = 51
+DIAGNOSTIC_SENSOR_FLOAT_ABSOLUTE_THROTTLE_POSITION_C = 52
+DIAGNOSTIC_SENSOR_FLOAT_ACCELERATOR_PEDAL_POSITION_D = 53
+DIAGNOSTIC_SENSOR_FLOAT_ACCELERATOR_PEDAL_POSITION_E = 54
+DIAGNOSTIC_SENSOR_FLOAT_ACCELERATOR_PEDAL_POSITION_F = 55
+DIAGNOSTIC_SENSOR_FLOAT_COMMANDED_THROTTLE_ACTUATOR = 56
+DIAGNOSTIC_SENSOR_FLOAT_ETHANOL_FUEL_PERCENTAGE = 57
+DIAGNOSTIC_SENSOR_FLOAT_ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 58
+DIAGNOSTIC_SENSOR_FLOAT_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 59
+DIAGNOSTIC_SENSOR_FLOAT_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 60
+DIAGNOSTIC_SENSOR_FLOAT_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 61
+DIAGNOSTIC_SENSOR_FLOAT_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 62
+DIAGNOSTIC_SENSOR_FLOAT_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 63
+DIAGNOSTIC_SENSOR_FLOAT_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 64
+DIAGNOSTIC_SENSOR_FLOAT_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 65
+DIAGNOSTIC_SENSOR_FLOAT_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 66
+DIAGNOSTIC_SENSOR_FLOAT_RELATIVE_ACCELERATOR_PEDAL_POSITION = 67
+DIAGNOSTIC_SENSOR_FLOAT_HYBRID_BATTERY_PACK_REMAINING_LIFE = 68
+DIAGNOSTIC_SENSOR_FLOAT_FUEL_INJECTION_TIMING = 69
+DIAGNOSTIC_SENSOR_FLOAT_ENGINE_FUEL_RATE = 70
+DIAGNOSTIC_SENSOR_FLOAT_LAST_SYSTEM_INDEX = DIAGNOSTIC_SENSOR_FLOAT_ENGINE_FUEL_RATE
+DIAGNOSTIC_SENSOR_FLOAT_VENDOR_START_INDEX = DIAGNOSTIC_SENSOR_FLOAT_LAST_SYSTEM_INDEX + 1
diff --git a/tools/emulator/vhal_const_generate.py b/tools/emulator/vhal_const_generate.py
index 6695bd3..55bcd1b 100755
--- a/tools/emulator/vhal_const_generate.py
+++ b/tools/emulator/vhal_const_generate.py
@@ -104,9 +104,9 @@
# Work around the fact that the parser doesn't (yet?) deal with inheritance.
# WARNING: This pattern is rather unsafe since we're not merging the lists as we should!
-vhal_21_doc['enums']['VehiclePropertyGroup'] = vhal_20_doc['enums']['VehiclePropertyGroup']
-vhal_21_doc['enums']['VehiclePropertyType'] = vhal_20_doc['enums']['VehiclePropertyType']
-vhal_21_doc['enums']['VehicleArea'] = vhal_20_doc['enums']['VehicleArea']
+# vhal_21_doc['enums']['VehiclePropertyGroup'] = vhal_20_doc['enums']['VehiclePropertyGroup']
+# vhal_21_doc['enums']['VehiclePropertyType'] = vhal_20_doc['enums']['VehiclePropertyType']
+# vhal_21_doc['enums']['VehicleArea'] = vhal_20_doc['enums']['VehicleArea']
def generateHal20():
print("********************************")
@@ -144,4 +144,4 @@
generateHal20()
-generateHal21()
+# generateHal21()
diff --git a/tools/emulator/vhal_consts_2_0.py b/tools/emulator/vhal_consts_2_0.py
index 50518fd..59c430c 100644
--- a/tools/emulator/vhal_consts_2_0.py
+++ b/tools/emulator/vhal_consts_2_0.py
@@ -15,18 +15,236 @@
# DO NOT EDIT MANUALLY
# This file was autogenerated by vhal_const_generate.py
-# VehicleApPowerSetState
-VEHICLEAPPOWERSETSTATE_BOOT_COMPLETE = 0x1
-VEHICLEAPPOWERSETSTATE_DEEP_SLEEP_ENTRY = 0x2
-VEHICLEAPPOWERSETSTATE_DEEP_SLEEP_EXIT = 0x3
-VEHICLEAPPOWERSETSTATE_SHUTDOWN_POSTPONE = 0x4
-VEHICLEAPPOWERSETSTATE_SHUTDOWN_START = 0x5
-VEHICLEAPPOWERSETSTATE_DISPLAY_OFF = 0x6
-VEHICLEAPPOWERSETSTATE_DISPLAY_ON = 0x7
+# VehicleAudioContextFlag
+VEHICLEAUDIOCONTEXTFLAG_MUSIC_FLAG = 0x1
+VEHICLEAUDIOCONTEXTFLAG_NAVIGATION_FLAG = 0x2
+VEHICLEAUDIOCONTEXTFLAG_VOICE_COMMAND_FLAG = 0x4
+VEHICLEAUDIOCONTEXTFLAG_CALL_FLAG = 0x8
+VEHICLEAUDIOCONTEXTFLAG_ALARM_FLAG = 0x10
+VEHICLEAUDIOCONTEXTFLAG_NOTIFICATION_FLAG = 0x20
+VEHICLEAUDIOCONTEXTFLAG_UNKNOWN_FLAG = 0x40
+VEHICLEAUDIOCONTEXTFLAG_SAFETY_ALERT_FLAG = 0x80
+VEHICLEAUDIOCONTEXTFLAG_CD_ROM_FLAG = 0x100
+VEHICLEAUDIOCONTEXTFLAG_AUX_AUDIO_FLAG = 0x200
+VEHICLEAUDIOCONTEXTFLAG_SYSTEM_SOUND_FLAG = 0x400
+VEHICLEAUDIOCONTEXTFLAG_RADIO_FLAG = 0x800
+VEHICLEAUDIOCONTEXTFLAG_EXT_SOURCE_FLAG = 0x1000
-# VehicleApPowerStateIndex
-VEHICLEAPPOWERSTATEINDEX_STATE = 0x0
-VEHICLEAPPOWERSTATEINDEX_ADDITIONAL = 0x1
+# VehicleAudioFocusIndex
+VEHICLEAUDIOFOCUSINDEX_FOCUS = 0x0
+VEHICLEAUDIOFOCUSINDEX_STREAMS = 0x1
+VEHICLEAUDIOFOCUSINDEX_EXTERNAL_FOCUS_STATE = 0x2
+VEHICLEAUDIOFOCUSINDEX_AUDIO_CONTEXTS = 0x3
+
+# Obd2CommonIgnitionMonitors
+OBD2COMMONIGNITIONMONITORS_COMPONENTS_AVAILABLE = 0x1
+OBD2COMMONIGNITIONMONITORS_COMPONENTS_INCOMPLETE = 0x2
+OBD2COMMONIGNITIONMONITORS_FUEL_SYSTEM_AVAILABLE = 0x4
+OBD2COMMONIGNITIONMONITORS_FUEL_SYSTEM_INCOMPLETE = 0x8
+OBD2COMMONIGNITIONMONITORS_MISFIRE_AVAILABLE = 0x10
+OBD2COMMONIGNITIONMONITORS_MISFIRE_INCOMPLETE = 0x20
+
+# Obd2FuelType
+OBD2FUELTYPE_NOT_AVAILABLE = 0x0
+OBD2FUELTYPE_GASOLINE = 0x1
+OBD2FUELTYPE_METHANOL = 0x2
+OBD2FUELTYPE_ETHANOL = 0x3
+OBD2FUELTYPE_DIESEL = 0x4
+OBD2FUELTYPE_LPG = 0x5
+OBD2FUELTYPE_CNG = 0x6
+OBD2FUELTYPE_PROPANE = 0x7
+OBD2FUELTYPE_ELECTRIC = 0x8
+OBD2FUELTYPE_BIFUEL_RUNNING_GASOLINE = 0x9
+OBD2FUELTYPE_BIFUEL_RUNNING_METHANOL = 0xa
+OBD2FUELTYPE_BIFUEL_RUNNING_ETHANOL = 0xb
+OBD2FUELTYPE_BIFUEL_RUNNING_LPG = 0xc
+OBD2FUELTYPE_BIFUEL_RUNNING_CNG = 0xd
+OBD2FUELTYPE_BIFUEL_RUNNING_PROPANE = 0xe
+OBD2FUELTYPE_BIFUEL_RUNNING_ELECTRIC = 0xf
+OBD2FUELTYPE_BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION = 0x10
+OBD2FUELTYPE_HYBRID_GASOLINE = 0x11
+OBD2FUELTYPE_HYBRID_ETHANOL = 0x12
+OBD2FUELTYPE_HYBRID_DIESEL = 0x13
+OBD2FUELTYPE_HYBRID_ELECTRIC = 0x14
+OBD2FUELTYPE_HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION = 0x15
+OBD2FUELTYPE_HYBRID_REGENERATIVE = 0x16
+OBD2FUELTYPE_BIFUEL_RUNNING_DIESEL = 0x17
+
+# VehicleAudioVolumeState
+VEHICLEAUDIOVOLUMESTATE_STATE_OK = 0x0
+VEHICLEAUDIOVOLUMESTATE_LIMIT_REACHED = 0x1
+
+# VmsMessageWithLayerIntegerValuesIndex
+VMSMESSAGEWITHLAYERINTEGERVALUESINDEX_LAYER_TYPE = 0x1
+VMSMESSAGEWITHLAYERINTEGERVALUESINDEX_LAYER_SUBTYPE = 0x2
+VMSMESSAGEWITHLAYERINTEGERVALUESINDEX_LAYER_VERSION = 0x3
+
+# Obd2SparkIgnitionMonitors
+OBD2SPARKIGNITIONMONITORS_EGR_AVAILABLE = 0x40
+OBD2SPARKIGNITIONMONITORS_EGR_INCOMPLETE = 0x80
+OBD2SPARKIGNITIONMONITORS_OXYGEN_SENSOR_HEATER_AVAILABLE = 0x100
+OBD2SPARKIGNITIONMONITORS_OXYGEN_SENSOR_HEATER_INCOMPLETE = 0x200
+OBD2SPARKIGNITIONMONITORS_OXYGEN_SENSOR_AVAILABLE = 0x400
+OBD2SPARKIGNITIONMONITORS_OXYGEN_SENSOR_INCOMPLETE = 0x800
+OBD2SPARKIGNITIONMONITORS_AC_REFRIGERANT_AVAILABLE = 0x1000
+OBD2SPARKIGNITIONMONITORS_AC_REFRIGERANT_INCOMPLETE = 0x2000
+OBD2SPARKIGNITIONMONITORS_SECONDARY_AIR_SYSTEM_AVAILABLE = 0x4000
+OBD2SPARKIGNITIONMONITORS_SECONDARY_AIR_SYSTEM_INCOMPLETE = 0x8000
+OBD2SPARKIGNITIONMONITORS_EVAPORATIVE_SYSTEM_AVAILABLE = 0x10000
+OBD2SPARKIGNITIONMONITORS_EVAPORATIVE_SYSTEM_INCOMPLETE = 0x20000
+OBD2SPARKIGNITIONMONITORS_HEATED_CATALYST_AVAILABLE = 0x40000
+OBD2SPARKIGNITIONMONITORS_HEATED_CATALYST_INCOMPLETE = 0x80000
+OBD2SPARKIGNITIONMONITORS_CATALYST_AVAILABLE = 0x100000
+OBD2SPARKIGNITIONMONITORS_CATALYST_INCOMPLETE = 0x200000
+
+# VehicleArea
+VEHICLEAREA_GLOBAL = 0x1000000
+VEHICLEAREA_ZONE = 0x2000000
+VEHICLEAREA_WINDOW = 0x3000000
+VEHICLEAREA_MIRROR = 0x4000000
+VEHICLEAREA_SEAT = 0x5000000
+VEHICLEAREA_DOOR = 0x6000000
+VEHICLEAREA_MASK = 0xf000000
+
+# Obd2SecondaryAirStatus
+OBD2SECONDARYAIRSTATUS_UPSTREAM = 0x1
+OBD2SECONDARYAIRSTATUS_DOWNSTREAM_OF_CATALYCIC_CONVERTER = 0x2
+OBD2SECONDARYAIRSTATUS_FROM_OUTSIDE_OR_OFF = 0x4
+OBD2SECONDARYAIRSTATUS_PUMP_ON_FOR_DIAGNOSTICS = 0x8
+
+# VehicleAudioHwVariantConfigFlag
+VEHICLEAUDIOHWVARIANTCONFIGFLAG_INTERNAL_RADIO_FLAG = 0x1
+
+# VmsMessageWithLayerAndPublisherIdIntegerValuesIndex
+VMSMESSAGEWITHLAYERANDPUBLISHERIDINTEGERVALUESINDEX_PUBLISHER_ID = 0x4
+
+# Obd2CompressionIgnitionMonitors
+OBD2COMPRESSIONIGNITIONMONITORS_EGR_OR_VVT_AVAILABLE = 0x40
+OBD2COMPRESSIONIGNITIONMONITORS_EGR_OR_VVT_INCOMPLETE = 0x80
+OBD2COMPRESSIONIGNITIONMONITORS_PM_FILTER_AVAILABLE = 0x100
+OBD2COMPRESSIONIGNITIONMONITORS_PM_FILTER_INCOMPLETE = 0x200
+OBD2COMPRESSIONIGNITIONMONITORS_EXHAUST_GAS_SENSOR_AVAILABLE = 0x400
+OBD2COMPRESSIONIGNITIONMONITORS_EXHAUST_GAS_SENSOR_INCOMPLETE = 0x800
+OBD2COMPRESSIONIGNITIONMONITORS_BOOST_PRESSURE_AVAILABLE = 0x1000
+OBD2COMPRESSIONIGNITIONMONITORS_BOOST_PRESSURE_INCOMPLETE = 0x2000
+OBD2COMPRESSIONIGNITIONMONITORS_NOx_SCR_AVAILABLE = 0x4000
+OBD2COMPRESSIONIGNITIONMONITORS_NOx_SCR_INCOMPLETE = 0x8000
+OBD2COMPRESSIONIGNITIONMONITORS_NMHC_CATALYST_AVAILABLE = 0x10000
+OBD2COMPRESSIONIGNITIONMONITORS_NMHC_CATALYST_INCOMPLETE = 0x20000
+
+# VmsOfferingMessageIntegerValuesIndex
+VMSOFFERINGMESSAGEINTEGERVALUESINDEX_PUBLISHER_ID = 0x1
+VMSOFFERINGMESSAGEINTEGERVALUESINDEX_NUMBER_OF_OFFERS = 0x2
+VMSOFFERINGMESSAGEINTEGERVALUESINDEX_OFFERING_START = 0x3
+
+# VehicleAreaZone
+VEHICLEAREAZONE_ROW_1_LEFT = 0x1
+VEHICLEAREAZONE_ROW_1_CENTER = 0x2
+VEHICLEAREAZONE_ROW_1_RIGHT = 0x4
+VEHICLEAREAZONE_ROW_1 = 0x8
+VEHICLEAREAZONE_ROW_2_LEFT = 0x10
+VEHICLEAREAZONE_ROW_2_CENTER = 0x20
+VEHICLEAREAZONE_ROW_2_RIGHT = 0x40
+VEHICLEAREAZONE_ROW_2 = 0x80
+VEHICLEAREAZONE_ROW_3_LEFT = 0x100
+VEHICLEAREAZONE_ROW_3_CENTER = 0x200
+VEHICLEAREAZONE_ROW_3_RIGHT = 0x400
+VEHICLEAREAZONE_ROW_3 = 0x800
+VEHICLEAREAZONE_ROW_4_LEFT = 0x1000
+VEHICLEAREAZONE_ROW_4_CENTER = 0x2000
+VEHICLEAREAZONE_ROW_4_RIGHT = 0x4000
+VEHICLEAREAZONE_ROW_4 = 0x8000
+VEHICLEAREAZONE_WHOLE_CABIN = 0x80000000
+
+# Obd2IgnitionMonitorKind
+OBD2IGNITIONMONITORKIND_SPARK = 0x0
+OBD2IGNITIONMONITORKIND_COMPRESSION = 0x1
+
+# VmsAvailabilityStateIntegerValuesIndex
+VMSAVAILABILITYSTATEINTEGERVALUESINDEX_SEQUENCE_NUMBER = 0x1
+VMSAVAILABILITYSTATEINTEGERVALUESINDEX_NUMBER_OF_ASSOCIATED_LAYERS = 0x2
+VMSAVAILABILITYSTATEINTEGERVALUESINDEX_LAYERS_START = 0x3
+
+# VehicleApPowerStateConfigFlag
+VEHICLEAPPOWERSTATECONFIGFLAG_ENABLE_DEEP_SLEEP_FLAG = 0x1
+VEHICLEAPPOWERSTATECONFIGFLAG_CONFIG_SUPPORT_TIMER_POWER_ON_FLAG = 0x2
+
+# VehicleHvacFanDirection
+VEHICLEHVACFANDIRECTION_FACE = 0x1
+VEHICLEHVACFANDIRECTION_FLOOR = 0x2
+VEHICLEHVACFANDIRECTION_FACE_AND_FLOOR = 0x3
+VEHICLEHVACFANDIRECTION_DEFROST = 0x4
+VEHICLEHVACFANDIRECTION_DEFROST_AND_FLOOR = 0x5
+
+# VehicleHwKeyInputAction
+VEHICLEHWKEYINPUTACTION_ACTION_DOWN = 0x0
+VEHICLEHWKEYINPUTACTION_ACTION_UP = 0x1
+
+# Wheel
+WHEEL_UNKNOWN = 0x0
+WHEEL_LEFT_FRONT = 0x1
+WHEEL_RIGHT_FRONT = 0x2
+WHEEL_LEFT_REAR = 0x4
+WHEEL_RIGHT_REAR = 0x8
+
+# VehicleAudioVolumeCapabilityFlag
+VEHICLEAUDIOVOLUMECAPABILITYFLAG_PERSISTENT_STORAGE = 0x1
+VEHICLEAUDIOVOLUMECAPABILITYFLAG_MASTER_VOLUME_ONLY = 0x2
+
+# VehicleAudioVolumeIndex
+VEHICLEAUDIOVOLUMEINDEX_INDEX_STREAM = 0x0
+VEHICLEAUDIOVOLUMEINDEX_INDEX_VOLUME = 0x1
+VEHICLEAUDIOVOLUMEINDEX_INDEX_STATE = 0x2
+
+# VehicleRadioConstants
+VEHICLERADIOCONSTANTS_VEHICLE_RADIO_PRESET_MIN_VALUE = 0x1
+
+# VehicleGear
+VEHICLEGEAR_GEAR_NEUTRAL = 0x1
+VEHICLEGEAR_GEAR_REVERSE = 0x2
+VEHICLEGEAR_GEAR_PARK = 0x4
+VEHICLEGEAR_GEAR_DRIVE = 0x8
+VEHICLEGEAR_GEAR_LOW = 0x10
+VEHICLEGEAR_GEAR_1 = 0x10
+VEHICLEGEAR_GEAR_2 = 0x20
+VEHICLEGEAR_GEAR_3 = 0x40
+VEHICLEGEAR_GEAR_4 = 0x80
+VEHICLEGEAR_GEAR_5 = 0x100
+VEHICLEGEAR_GEAR_6 = 0x200
+VEHICLEGEAR_GEAR_7 = 0x400
+VEHICLEGEAR_GEAR_8 = 0x800
+VEHICLEGEAR_GEAR_9 = 0x1000
+
+# VehicleDrivingStatus
+VEHICLEDRIVINGSTATUS_UNRESTRICTED = 0x0
+VEHICLEDRIVINGSTATUS_NO_VIDEO = 0x1
+VEHICLEDRIVINGSTATUS_NO_KEYBOARD_INPUT = 0x2
+VEHICLEDRIVINGSTATUS_NO_VOICE_INPUT = 0x4
+VEHICLEDRIVINGSTATUS_NO_CONFIG = 0x8
+VEHICLEDRIVINGSTATUS_LIMIT_MESSAGE_LEN = 0x10
+
+# VehicleAudioRoutingPolicyIndex
+VEHICLEAUDIOROUTINGPOLICYINDEX_STREAM = 0x0
+VEHICLEAUDIOROUTINGPOLICYINDEX_CONTEXTS = 0x1
+
+# VmsSubscriptionsStateIntegerValuesIndex
+VMSSUBSCRIPTIONSSTATEINTEGERVALUESINDEX_SEQUENCE_NUMBER = 0x1
+VMSSUBSCRIPTIONSSTATEINTEGERVALUESINDEX_NUMBER_OF_LAYERS = 0x2
+VMSSUBSCRIPTIONSSTATEINTEGERVALUESINDEX_NUMBER_OF_ASSOCIATED_LAYERS = 0x3
+VMSSUBSCRIPTIONSSTATEINTEGERVALUESINDEX_SUBSCRIPTIONS_START = 0x4
+
+# SubscribeFlags
+SUBSCRIBEFLAGS_UNDEFINED = 0x0
+SUBSCRIBEFLAGS_HAL_EVENT = 0x1
+SUBSCRIBEFLAGS_SET_CALL = 0x2
+SUBSCRIBEFLAGS_DEFAULT = 0x1
+
+# VehicleAudioExtFocusFlag
+VEHICLEAUDIOEXTFOCUSFLAG_NONE_FLAG = 0x0
+VEHICLEAUDIOEXTFOCUSFLAG_PERMANENT_FLAG = 0x1
+VEHICLEAUDIOEXTFOCUSFLAG_TRANSIENT_FLAG = 0x2
+VEHICLEAUDIOEXTFOCUSFLAG_PLAY_ONLY_FLAG = 0x4
+VEHICLEAUDIOEXTFOCUSFLAG_MUTE_MEDIA_FLAG = 0x8
# VehicleAudioFocusRequest
VEHICLEAUDIOFOCUSREQUEST_REQUEST_GAIN = 0x1
@@ -35,18 +253,197 @@
VEHICLEAUDIOFOCUSREQUEST_REQUEST_GAIN_TRANSIENT_NO_DUCK = 0x4
VEHICLEAUDIOFOCUSREQUEST_REQUEST_RELEASE = 0x5
-# VehicleDisplay
-VEHICLEDISPLAY_MAIN = 0x0
-VEHICLEDISPLAY_INSTRUMENT_CLUSTER = 0x1
+# VehiclePropertyType
+VEHICLEPROPERTYTYPE_STRING = 0x100000
+VEHICLEPROPERTYTYPE_BOOLEAN = 0x200000
+VEHICLEPROPERTYTYPE_INT32 = 0x400000
+VEHICLEPROPERTYTYPE_INT32_VEC = 0x410000
+VEHICLEPROPERTYTYPE_INT64 = 0x500000
+VEHICLEPROPERTYTYPE_FLOAT = 0x600000
+VEHICLEPROPERTYTYPE_FLOAT_VEC = 0x610000
+VEHICLEPROPERTYTYPE_BYTES = 0x700000
+VEHICLEPROPERTYTYPE_COMPLEX = 0xe00000
+VEHICLEPROPERTYTYPE_MASK = 0xff0000
-# VehicleRadioConstants
-VEHICLERADIOCONSTANTS_VEHICLE_RADIO_PRESET_MIN_VALUE = 0x1
+# VehiclePropertyAccess
+VEHICLEPROPERTYACCESS_NONE = 0x0
+VEHICLEPROPERTYACCESS_READ = 0x1
+VEHICLEPROPERTYACCESS_WRITE = 0x2
+VEHICLEPROPERTYACCESS_READ_WRITE = 0x3
-# VehicleAudioFocusIndex
-VEHICLEAUDIOFOCUSINDEX_FOCUS = 0x0
-VEHICLEAUDIOFOCUSINDEX_STREAMS = 0x1
-VEHICLEAUDIOFOCUSINDEX_EXTERNAL_FOCUS_STATE = 0x2
-VEHICLEAUDIOFOCUSINDEX_AUDIO_CONTEXTS = 0x3
+# Obd2FuelSystemStatus
+OBD2FUELSYSTEMSTATUS_OPEN_INSUFFICIENT_ENGINE_TEMPERATURE = 0x1
+OBD2FUELSYSTEMSTATUS_CLOSED_LOOP = 0x2
+OBD2FUELSYSTEMSTATUS_OPEN_ENGINE_LOAD_OR_DECELERATION = 0x4
+OBD2FUELSYSTEMSTATUS_OPEN_SYSTEM_FAILURE = 0x8
+OBD2FUELSYSTEMSTATUS_CLOSED_LOOP_BUT_FEEDBACK_FAULT = 0x10
+
+# VehicleTurnSignal
+VEHICLETURNSIGNAL_NONE = 0x0
+VEHICLETURNSIGNAL_RIGHT = 0x1
+VEHICLETURNSIGNAL_LEFT = 0x2
+VEHICLETURNSIGNAL_EMERGENCY = 0x4
+
+# VehicleAudioStreamFlag
+VEHICLEAUDIOSTREAMFLAG_STREAM0_FLAG = 0x1
+VEHICLEAUDIOSTREAMFLAG_STREAM1_FLAG = 0x2
+VEHICLEAUDIOSTREAMFLAG_STREAM2_FLAG = 0x4
+
+# VehicleApPowerBootupReason
+VEHICLEAPPOWERBOOTUPREASON_USER_POWER_ON = 0x0
+VEHICLEAPPOWERBOOTUPREASON_USER_UNLOCK = 0x1
+VEHICLEAPPOWERBOOTUPREASON_TIMER = 0x2
+
+# VehicleAudioFocusState
+VEHICLEAUDIOFOCUSSTATE_STATE_GAIN = 0x1
+VEHICLEAUDIOFOCUSSTATE_STATE_GAIN_TRANSIENT = 0x2
+VEHICLEAUDIOFOCUSSTATE_STATE_LOSS_TRANSIENT_CAN_DUCK = 0x3
+VEHICLEAUDIOFOCUSSTATE_STATE_LOSS_TRANSIENT = 0x4
+VEHICLEAUDIOFOCUSSTATE_STATE_LOSS = 0x5
+VEHICLEAUDIOFOCUSSTATE_STATE_LOSS_TRANSIENT_EXLCUSIVE = 0x6
+
+# VehicleAudioStream
+VEHICLEAUDIOSTREAM_STREAM0 = 0x0
+VEHICLEAUDIOSTREAM_STREAM1 = 0x1
+
+# DiagnosticFloatSensorIndex
+DIAGNOSTICFLOATSENSORINDEX_CALCULATED_ENGINE_LOAD = 0x0
+DIAGNOSTICFLOATSENSORINDEX_ENGINE_COOLANT_TEMPERATURE = 0x1
+DIAGNOSTICFLOATSENSORINDEX_SHORT_TERM_FUEL_TRIM_BANK1 = 0x2
+DIAGNOSTICFLOATSENSORINDEX_LONG_TERM_FUEL_TRIM_BANK1 = 0x3
+DIAGNOSTICFLOATSENSORINDEX_SHORT_TERM_FUEL_TRIM_BANK2 = 0x4
+DIAGNOSTICFLOATSENSORINDEX_LONG_TERM_FUEL_TRIM_BANK2 = 0x5
+DIAGNOSTICFLOATSENSORINDEX_FUEL_PRESSURE = 0x6
+DIAGNOSTICFLOATSENSORINDEX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 0x7
+DIAGNOSTICFLOATSENSORINDEX_ENGINE_RPM = 0x8
+DIAGNOSTICFLOATSENSORINDEX_VEHICLE_SPEED = 0x9
+DIAGNOSTICFLOATSENSORINDEX_TIMING_ADVANCE = 0xa
+DIAGNOSTICFLOATSENSORINDEX_MAF_AIR_FLOW_RATE = 0xb
+DIAGNOSTICFLOATSENSORINDEX_THROTTLE_POSITION = 0xc
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR1_VOLTAGE = 0xd
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM = 0xe
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO = 0xf
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR2_VOLTAGE = 0x10
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM = 0x11
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO = 0x12
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR3_VOLTAGE = 0x13
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM = 0x14
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO = 0x15
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR4_VOLTAGE = 0x16
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM = 0x17
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO = 0x18
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR5_VOLTAGE = 0x19
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM = 0x1a
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO = 0x1b
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR6_VOLTAGE = 0x1c
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM = 0x1d
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO = 0x1e
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR7_VOLTAGE = 0x1f
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM = 0x20
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO = 0x21
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR8_VOLTAGE = 0x22
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM = 0x23
+DIAGNOSTICFLOATSENSORINDEX_OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO = 0x24
+DIAGNOSTICFLOATSENSORINDEX_FUEL_RAIL_PRESSURE = 0x25
+DIAGNOSTICFLOATSENSORINDEX_FUEL_RAIL_GAUGE_PRESSURE = 0x26
+DIAGNOSTICFLOATSENSORINDEX_COMMANDED_EXHAUST_GAS_RECIRCULATION = 0x27
+DIAGNOSTICFLOATSENSORINDEX_EXHAUST_GAS_RECIRCULATION_ERROR = 0x28
+DIAGNOSTICFLOATSENSORINDEX_COMMANDED_EVAPORATIVE_PURGE = 0x29
+DIAGNOSTICFLOATSENSORINDEX_FUEL_TANK_LEVEL_INPUT = 0x2a
+DIAGNOSTICFLOATSENSORINDEX_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 0x2b
+DIAGNOSTICFLOATSENSORINDEX_CATALYST_TEMPERATURE_BANK1_SENSOR1 = 0x2c
+DIAGNOSTICFLOATSENSORINDEX_CATALYST_TEMPERATURE_BANK2_SENSOR1 = 0x2d
+DIAGNOSTICFLOATSENSORINDEX_CATALYST_TEMPERATURE_BANK1_SENSOR2 = 0x2e
+DIAGNOSTICFLOATSENSORINDEX_CATALYST_TEMPERATURE_BANK2_SENSOR2 = 0x2f
+DIAGNOSTICFLOATSENSORINDEX_ABSOLUTE_LOAD_VALUE = 0x30
+DIAGNOSTICFLOATSENSORINDEX_FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO = 0x31
+DIAGNOSTICFLOATSENSORINDEX_RELATIVE_THROTTLE_POSITION = 0x32
+DIAGNOSTICFLOATSENSORINDEX_ABSOLUTE_THROTTLE_POSITION_B = 0x33
+DIAGNOSTICFLOATSENSORINDEX_ABSOLUTE_THROTTLE_POSITION_C = 0x34
+DIAGNOSTICFLOATSENSORINDEX_ACCELERATOR_PEDAL_POSITION_D = 0x35
+DIAGNOSTICFLOATSENSORINDEX_ACCELERATOR_PEDAL_POSITION_E = 0x36
+DIAGNOSTICFLOATSENSORINDEX_ACCELERATOR_PEDAL_POSITION_F = 0x37
+DIAGNOSTICFLOATSENSORINDEX_COMMANDED_THROTTLE_ACTUATOR = 0x38
+DIAGNOSTICFLOATSENSORINDEX_ETHANOL_FUEL_PERCENTAGE = 0x39
+DIAGNOSTICFLOATSENSORINDEX_ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 0x3a
+DIAGNOSTICFLOATSENSORINDEX_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 0x3b
+DIAGNOSTICFLOATSENSORINDEX_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 0x3c
+DIAGNOSTICFLOATSENSORINDEX_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 0x3d
+DIAGNOSTICFLOATSENSORINDEX_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 0x3e
+DIAGNOSTICFLOATSENSORINDEX_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 0x3f
+DIAGNOSTICFLOATSENSORINDEX_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 0x40
+DIAGNOSTICFLOATSENSORINDEX_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 0x41
+DIAGNOSTICFLOATSENSORINDEX_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 0x42
+DIAGNOSTICFLOATSENSORINDEX_RELATIVE_ACCELERATOR_PEDAL_POSITION = 0x43
+DIAGNOSTICFLOATSENSORINDEX_HYBRID_BATTERY_PACK_REMAINING_LIFE = 0x44
+DIAGNOSTICFLOATSENSORINDEX_FUEL_INJECTION_TIMING = 0x45
+DIAGNOSTICFLOATSENSORINDEX_ENGINE_FUEL_RATE = 0x46
+DIAGNOSTICFLOATSENSORINDEX_LAST_SYSTEM_INDEX = 0x46
+
+# VehicleAreaMirror
+VEHICLEAREAMIRROR_DRIVER_LEFT = 0x1
+VEHICLEAREAMIRROR_DRIVER_RIGHT = 0x2
+VEHICLEAREAMIRROR_DRIVER_CENTER = 0x4
+
+# VehicleAreaWindow
+VEHICLEAREAWINDOW_FRONT_WINDSHIELD = 0x1
+VEHICLEAREAWINDOW_REAR_WINDSHIELD = 0x2
+VEHICLEAREAWINDOW_ROOF_TOP = 0x4
+VEHICLEAREAWINDOW_ROW_1_LEFT = 0x10
+VEHICLEAREAWINDOW_ROW_1_RIGHT = 0x20
+VEHICLEAREAWINDOW_ROW_2_LEFT = 0x100
+VEHICLEAREAWINDOW_ROW_2_RIGHT = 0x200
+VEHICLEAREAWINDOW_ROW_3_LEFT = 0x1000
+VEHICLEAREAWINDOW_ROW_3_RIGHT = 0x2000
+
+# VehiclePropertyOperation
+VEHICLEPROPERTYOPERATION_GENERIC = 0x0
+VEHICLEPROPERTYOPERATION_SET = 0x1
+VEHICLEPROPERTYOPERATION_GET = 0x2
+VEHICLEPROPERTYOPERATION_SUBSCRIBE = 0x3
+
+# VehicleAreaDoor
+VEHICLEAREADOOR_ROW_1_LEFT = 0x1
+VEHICLEAREADOOR_ROW_1_RIGHT = 0x4
+VEHICLEAREADOOR_ROW_2_LEFT = 0x10
+VEHICLEAREADOOR_ROW_2_RIGHT = 0x40
+VEHICLEAREADOOR_ROW_3_LEFT = 0x100
+VEHICLEAREADOOR_ROW_3_RIGHT = 0x400
+VEHICLEAREADOOR_HOOD = 0x10000000
+VEHICLEAREADOOR_REAR = 0x20000000
+
+# StatusCode
+STATUSCODE_OK = 0x0
+STATUSCODE_TRY_AGAIN = 0x1
+STATUSCODE_INVALID_ARG = 0x2
+STATUSCODE_NOT_AVAILABLE = 0x3
+STATUSCODE_ACCESS_DENIED = 0x4
+STATUSCODE_INTERNAL_ERROR = 0x5
+
+# VehicleAreaSeat
+VEHICLEAREASEAT_ROW_1_LEFT = 0x1
+VEHICLEAREASEAT_ROW_1_CENTER = 0x2
+VEHICLEAREASEAT_ROW_1_RIGHT = 0x4
+VEHICLEAREASEAT_ROW_2_LEFT = 0x10
+VEHICLEAREASEAT_ROW_2_CENTER = 0x20
+VEHICLEAREASEAT_ROW_2_RIGHT = 0x40
+VEHICLEAREASEAT_ROW_3_LEFT = 0x100
+VEHICLEAREASEAT_ROW_3_CENTER = 0x200
+VEHICLEAREASEAT_ROW_3_RIGHT = 0x400
+
+# VmsMessageType
+VMSMESSAGETYPE_SUBSCRIBE = 0x1
+VMSMESSAGETYPE_SUBSCRIBE_TO_PUBLISHER = 0x2
+VMSMESSAGETYPE_UNSUBSCRIBE = 0x3
+VMSMESSAGETYPE_UNSUBSCRIBE_TO_PUBLISHER = 0x4
+VMSMESSAGETYPE_OFFERING = 0x5
+VMSMESSAGETYPE_AVAILABILITY_REQUEST = 0x6
+VMSMESSAGETYPE_SUBSCRIPTIONS_REQUEST = 0x7
+VMSMESSAGETYPE_AVAILABILITY_RESPONSE = 0x8
+VMSMESSAGETYPE_AVAILABILITY_CHANGE = 0x9
+VMSMESSAGETYPE_SUBSCRIPTIONS_RESPONSE = 0xa
+VMSMESSAGETYPE_SUBSCRIPTIONS_CHANGE = 0xb
+VMSMESSAGETYPE_DATA = 0xc
# VehicleProperty
VEHICLEPROPERTY_INVALID = 0x0
@@ -147,59 +544,75 @@
VEHICLEPROPERTY_WINDOW_VENT_POS = 0x11400bc2
VEHICLEPROPERTY_WINDOW_VENT_MOVE = 0x11400bc3
VEHICLEPROPERTY_WINDOW_LOCK = 0x11200bc4
+VEHICLEPROPERTY_WHEEL_TICK = 0x11e00306
+VEHICLEPROPERTY_ABS_ACTIVE = 0x1120040a
+VEHICLEPROPERTY_TRACTION_CONTROL_ACTIVE = 0x1120040b
+VEHICLEPROPERTY_HVAC_AUTO_RECIRC_ON = 0x12200512
+VEHICLEPROPERTY_VEHICLE_MAP_SERVICE = 0x11e00c00
+VEHICLEPROPERTY_OBD2_LIVE_FRAME = 0x11e00d00
+VEHICLEPROPERTY_OBD2_FREEZE_FRAME = 0x11e00d01
+VEHICLEPROPERTY_OBD2_FREEZE_FRAME_INFO = 0x11e00d02
+VEHICLEPROPERTY_OBD2_FREEZE_FRAME_CLEAR = 0x11e00d03
-# VehicleAreaZone
-VEHICLEAREAZONE_ROW_1_LEFT = 0x1
-VEHICLEAREAZONE_ROW_1_CENTER = 0x2
-VEHICLEAREAZONE_ROW_1_RIGHT = 0x4
-VEHICLEAREAZONE_ROW_1 = 0x8
-VEHICLEAREAZONE_ROW_2_LEFT = 0x10
-VEHICLEAREAZONE_ROW_2_CENTER = 0x20
-VEHICLEAREAZONE_ROW_2_RIGHT = 0x40
-VEHICLEAREAZONE_ROW_2 = 0x80
-VEHICLEAREAZONE_ROW_3_LEFT = 0x100
-VEHICLEAREAZONE_ROW_3_CENTER = 0x200
-VEHICLEAREAZONE_ROW_3_RIGHT = 0x400
-VEHICLEAREAZONE_ROW_3 = 0x800
-VEHICLEAREAZONE_ROW_4_LEFT = 0x1000
-VEHICLEAREAZONE_ROW_4_CENTER = 0x2000
-VEHICLEAREAZONE_ROW_4_RIGHT = 0x4000
-VEHICLEAREAZONE_ROW_4 = 0x8000
-VEHICLEAREAZONE_WHOLE_CABIN = 0x80000000
-
-# SubscribeFlags
-SUBSCRIBEFLAGS_UNDEFINED = 0x0
-SUBSCRIBEFLAGS_HAL_EVENT = 0x1
-SUBSCRIBEFLAGS_SET_CALL = 0x2
-SUBSCRIBEFLAGS_DEFAULT = 0x1
-
-# Wheel
-WHEEL_UNKNOWN = 0x0
-WHEEL_LEFT_FRONT = 0x1
-WHEEL_RIGHT_FRONT = 0x2
-WHEEL_LEFT_REAR = 0x4
-WHEEL_RIGHT_REAR = 0x8
-
-# StatusCode
-STATUSCODE_OK = 0x0
-STATUSCODE_TRY_AGAIN = 0x1
-STATUSCODE_INVALID_ARG = 0x2
-STATUSCODE_NOT_AVAILABLE = 0x3
-STATUSCODE_ACCESS_DENIED = 0x4
-STATUSCODE_INTERNAL_ERROR = 0x5
-
-# VehicleAudioHwVariantConfigFlag
-VEHICLEAUDIOHWVARIANTCONFIGFLAG_INTERNAL_RADIO_FLAG = 0x1
+# VehicleIgnitionState
+VEHICLEIGNITIONSTATE_UNDEFINED = 0x0
+VEHICLEIGNITIONSTATE_LOCK = 0x1
+VEHICLEIGNITIONSTATE_OFF = 0x2
+VEHICLEIGNITIONSTATE_ACC = 0x3
+VEHICLEIGNITIONSTATE_ON = 0x4
+VEHICLEIGNITIONSTATE_START = 0x5
# VehiclePropertyGroup
VEHICLEPROPERTYGROUP_SYSTEM = 0x10000000
VEHICLEPROPERTYGROUP_VENDOR = 0x20000000
VEHICLEPROPERTYGROUP_MASK = 0xf0000000
-# VehicleAudioStreamFlag
-VEHICLEAUDIOSTREAMFLAG_STREAM0_FLAG = 0x1
-VEHICLEAUDIOSTREAMFLAG_STREAM1_FLAG = 0x2
-VEHICLEAUDIOSTREAMFLAG_STREAM2_FLAG = 0x4
+# VehicleApPowerSetState
+VEHICLEAPPOWERSETSTATE_BOOT_COMPLETE = 0x1
+VEHICLEAPPOWERSETSTATE_DEEP_SLEEP_ENTRY = 0x2
+VEHICLEAPPOWERSETSTATE_DEEP_SLEEP_EXIT = 0x3
+VEHICLEAPPOWERSETSTATE_SHUTDOWN_POSTPONE = 0x4
+VEHICLEAPPOWERSETSTATE_SHUTDOWN_START = 0x5
+VEHICLEAPPOWERSETSTATE_DISPLAY_OFF = 0x6
+VEHICLEAPPOWERSETSTATE_DISPLAY_ON = 0x7
+
+# VmsBaseMessageIntegerValuesIndex
+VMSBASEMESSAGEINTEGERVALUESINDEX_MESSAGE_TYPE = 0x0
+
+# DiagnosticIntegerSensorIndex
+DIAGNOSTICINTEGERSENSORINDEX_FUEL_SYSTEM_STATUS = 0x0
+DIAGNOSTICINTEGERSENSORINDEX_MALFUNCTION_INDICATOR_LIGHT_ON = 0x1
+DIAGNOSTICINTEGERSENSORINDEX_IGNITION_MONITORS_SUPPORTED = 0x2
+DIAGNOSTICINTEGERSENSORINDEX_IGNITION_SPECIFIC_MONITORS = 0x3
+DIAGNOSTICINTEGERSENSORINDEX_INTAKE_AIR_TEMPERATURE = 0x4
+DIAGNOSTICINTEGERSENSORINDEX_COMMANDED_SECONDARY_AIR_STATUS = 0x5
+DIAGNOSTICINTEGERSENSORINDEX_NUM_OXYGEN_SENSORS_PRESENT = 0x6
+DIAGNOSTICINTEGERSENSORINDEX_RUNTIME_SINCE_ENGINE_START = 0x7
+DIAGNOSTICINTEGERSENSORINDEX_DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON = 0x8
+DIAGNOSTICINTEGERSENSORINDEX_WARMUPS_SINCE_CODES_CLEARED = 0x9
+DIAGNOSTICINTEGERSENSORINDEX_DISTANCE_TRAVELED_SINCE_CODES_CLEARED = 0xa
+DIAGNOSTICINTEGERSENSORINDEX_ABSOLUTE_BAROMETRIC_PRESSURE = 0xb
+DIAGNOSTICINTEGERSENSORINDEX_CONTROL_MODULE_VOLTAGE = 0xc
+DIAGNOSTICINTEGERSENSORINDEX_AMBIENT_AIR_TEMPERATURE = 0xd
+DIAGNOSTICINTEGERSENSORINDEX_TIME_WITH_MALFUNCTION_LIGHT_ON = 0xe
+DIAGNOSTICINTEGERSENSORINDEX_TIME_SINCE_TROUBLE_CODES_CLEARED = 0xf
+DIAGNOSTICINTEGERSENSORINDEX_MAX_FUEL_AIR_EQUIVALENCE_RATIO = 0x10
+DIAGNOSTICINTEGERSENSORINDEX_MAX_OXYGEN_SENSOR_VOLTAGE = 0x11
+DIAGNOSTICINTEGERSENSORINDEX_MAX_OXYGEN_SENSOR_CURRENT = 0x12
+DIAGNOSTICINTEGERSENSORINDEX_MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 0x13
+DIAGNOSTICINTEGERSENSORINDEX_MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR = 0x14
+DIAGNOSTICINTEGERSENSORINDEX_FUEL_TYPE = 0x15
+DIAGNOSTICINTEGERSENSORINDEX_FUEL_RAIL_ABSOLUTE_PRESSURE = 0x16
+DIAGNOSTICINTEGERSENSORINDEX_ENGINE_OIL_TEMPERATURE = 0x17
+DIAGNOSTICINTEGERSENSORINDEX_DRIVER_DEMAND_PERCENT_TORQUE = 0x18
+DIAGNOSTICINTEGERSENSORINDEX_ENGINE_ACTUAL_PERCENT_TORQUE = 0x19
+DIAGNOSTICINTEGERSENSORINDEX_ENGINE_REFERENCE_PERCENT_TORQUE = 0x1a
+DIAGNOSTICINTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_IDLE = 0x1b
+DIAGNOSTICINTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_POINT1 = 0x1c
+DIAGNOSTICINTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_POINT2 = 0x1d
+DIAGNOSTICINTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_POINT3 = 0x1e
+DIAGNOSTICINTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_POINT4 = 0x1f
+DIAGNOSTICINTEGERSENSORINDEX_LAST_SYSTEM_INDEX = 0x1f
# VehiclePropertyChangeMode
VEHICLEPROPERTYCHANGEMODE_STATIC = 0x0
@@ -208,21 +621,17 @@
VEHICLEPROPERTYCHANGEMODE_POLL = 0x3
VEHICLEPROPERTYCHANGEMODE_ON_SET = 0x4
-# VehicleAreaSeat
-VEHICLEAREASEAT_ROW_1_LEFT = 0x1
-VEHICLEAREASEAT_ROW_1_CENTER = 0x2
-VEHICLEAREASEAT_ROW_1_RIGHT = 0x4
-VEHICLEAREASEAT_ROW_2_LEFT = 0x10
-VEHICLEAREASEAT_ROW_2_CENTER = 0x20
-VEHICLEAREASEAT_ROW_2_RIGHT = 0x40
-VEHICLEAREASEAT_ROW_3_LEFT = 0x100
-VEHICLEAREASEAT_ROW_3_CENTER = 0x200
-VEHICLEAREASEAT_ROW_3_RIGHT = 0x400
+# VehicleApPowerState
+VEHICLEAPPOWERSTATE_OFF = 0x0
+VEHICLEAPPOWERSTATE_DEEP_SLEEP = 0x1
+VEHICLEAPPOWERSTATE_ON_DISP_OFF = 0x2
+VEHICLEAPPOWERSTATE_ON_FULL = 0x3
+VEHICLEAPPOWERSTATE_SHUTDOWN_PREPARE = 0x4
-# VehicleAudioVolumeIndex
-VEHICLEAUDIOVOLUMEINDEX_INDEX_STREAM = 0x0
-VEHICLEAUDIOVOLUMEINDEX_INDEX_VOLUME = 0x1
-VEHICLEAUDIOVOLUMEINDEX_INDEX_STATE = 0x2
+# VehicleInstrumentClusterType
+VEHICLEINSTRUMENTCLUSTERTYPE_NONE = 0x0
+VEHICLEINSTRUMENTCLUSTERTYPE_HAL_INTERFACE = 0x1
+VEHICLEINSTRUMENTCLUSTERTYPE_EXTERNAL_DISPLAY = 0x2
# VehicleUnit
VEHICLEUNIT_SHOULD_NOT_USE = 0x0
@@ -241,189 +650,22 @@
VEHICLEUNIT_SECS = 0x53
VEHICLEUNIT_YEAR = 0x59
-# VehicleAreaMirror
-VEHICLEAREAMIRROR_DRIVER_LEFT = 0x1
-VEHICLEAREAMIRROR_DRIVER_RIGHT = 0x2
-VEHICLEAREAMIRROR_DRIVER_CENTER = 0x4
-
-# VehiclePropertyAccess
-VEHICLEPROPERTYACCESS_NONE = 0x0
-VEHICLEPROPERTYACCESS_READ = 0x1
-VEHICLEPROPERTYACCESS_WRITE = 0x2
-VEHICLEPROPERTYACCESS_READ_WRITE = 0x3
-
-# VehicleAudioContextFlag
-VEHICLEAUDIOCONTEXTFLAG_MUSIC_FLAG = 0x1
-VEHICLEAUDIOCONTEXTFLAG_NAVIGATION_FLAG = 0x2
-VEHICLEAUDIOCONTEXTFLAG_VOICE_COMMAND_FLAG = 0x4
-VEHICLEAUDIOCONTEXTFLAG_CALL_FLAG = 0x8
-VEHICLEAUDIOCONTEXTFLAG_ALARM_FLAG = 0x10
-VEHICLEAUDIOCONTEXTFLAG_NOTIFICATION_FLAG = 0x20
-VEHICLEAUDIOCONTEXTFLAG_UNKNOWN_FLAG = 0x40
-VEHICLEAUDIOCONTEXTFLAG_SAFETY_ALERT_FLAG = 0x80
-VEHICLEAUDIOCONTEXTFLAG_CD_ROM_FLAG = 0x100
-VEHICLEAUDIOCONTEXTFLAG_AUX_AUDIO_FLAG = 0x200
-VEHICLEAUDIOCONTEXTFLAG_SYSTEM_SOUND_FLAG = 0x400
-VEHICLEAUDIOCONTEXTFLAG_RADIO_FLAG = 0x800
-VEHICLEAUDIOCONTEXTFLAG_EXT_SOURCE_FLAG = 0x1000
-
-# VehicleDrivingStatus
-VEHICLEDRIVINGSTATUS_UNRESTRICTED = 0x0
-VEHICLEDRIVINGSTATUS_NO_VIDEO = 0x1
-VEHICLEDRIVINGSTATUS_NO_KEYBOARD_INPUT = 0x2
-VEHICLEDRIVINGSTATUS_NO_VOICE_INPUT = 0x4
-VEHICLEDRIVINGSTATUS_NO_CONFIG = 0x8
-VEHICLEDRIVINGSTATUS_LIMIT_MESSAGE_LEN = 0x10
-
-# VehicleGear
-VEHICLEGEAR_GEAR_NEUTRAL = 0x1
-VEHICLEGEAR_GEAR_REVERSE = 0x2
-VEHICLEGEAR_GEAR_PARK = 0x4
-VEHICLEGEAR_GEAR_DRIVE = 0x8
-VEHICLEGEAR_GEAR_LOW = 0x10
-VEHICLEGEAR_GEAR_1 = 0x10
-VEHICLEGEAR_GEAR_2 = 0x20
-VEHICLEGEAR_GEAR_3 = 0x40
-VEHICLEGEAR_GEAR_4 = 0x80
-VEHICLEGEAR_GEAR_5 = 0x100
-VEHICLEGEAR_GEAR_6 = 0x200
-VEHICLEGEAR_GEAR_7 = 0x400
-VEHICLEGEAR_GEAR_8 = 0x800
-VEHICLEGEAR_GEAR_9 = 0x1000
-
-# VehicleTurnSignal
-VEHICLETURNSIGNAL_NONE = 0x0
-VEHICLETURNSIGNAL_RIGHT = 0x1
-VEHICLETURNSIGNAL_LEFT = 0x2
-VEHICLETURNSIGNAL_EMERGENCY = 0x4
+# VehicleAudioVolumeLimitIndex
+VEHICLEAUDIOVOLUMELIMITINDEX_STREAM = 0x0
+VEHICLEAUDIOVOLUMELIMITINDEX_MAX_VOLUME = 0x1
# VehicleApPowerStateShutdownParam
VEHICLEAPPOWERSTATESHUTDOWNPARAM_SHUTDOWN_IMMEDIATELY = 0x1
VEHICLEAPPOWERSTATESHUTDOWNPARAM_CAN_SLEEP = 0x2
VEHICLEAPPOWERSTATESHUTDOWNPARAM_SHUTDOWN_ONLY = 0x3
-# VehiclePropertyOperation
-VEHICLEPROPERTYOPERATION_GENERIC = 0x0
-VEHICLEPROPERTYOPERATION_SET = 0x1
-VEHICLEPROPERTYOPERATION_GET = 0x2
-VEHICLEPROPERTYOPERATION_SUBSCRIBE = 0x3
+# VehicleApPowerStateIndex
+VEHICLEAPPOWERSTATEINDEX_STATE = 0x0
+VEHICLEAPPOWERSTATEINDEX_ADDITIONAL = 0x1
-# VehiclePropertyType
-VEHICLEPROPERTYTYPE_STRING = 0x100000
-VEHICLEPROPERTYTYPE_BOOLEAN = 0x200000
-VEHICLEPROPERTYTYPE_INT32 = 0x400000
-VEHICLEPROPERTYTYPE_INT32_VEC = 0x410000
-VEHICLEPROPERTYTYPE_INT64 = 0x500000
-VEHICLEPROPERTYTYPE_FLOAT = 0x600000
-VEHICLEPROPERTYTYPE_FLOAT_VEC = 0x610000
-VEHICLEPROPERTYTYPE_BYTES = 0x700000
-VEHICLEPROPERTYTYPE_COMPLEX = 0xe00000
-VEHICLEPROPERTYTYPE_MASK = 0xff0000
-
-# VehicleAreaDoor
-VEHICLEAREADOOR_ROW_1_LEFT = 0x1
-VEHICLEAREADOOR_ROW_1_RIGHT = 0x4
-VEHICLEAREADOOR_ROW_2_LEFT = 0x10
-VEHICLEAREADOOR_ROW_2_RIGHT = 0x40
-VEHICLEAREADOOR_ROW_3_LEFT = 0x100
-VEHICLEAREADOOR_ROW_3_RIGHT = 0x400
-VEHICLEAREADOOR_HOOD = 0x10000000
-VEHICLEAREADOOR_REAR = 0x20000000
-
-# VehicleHwKeyInputAction
-VEHICLEHWKEYINPUTACTION_ACTION_DOWN = 0x0
-VEHICLEHWKEYINPUTACTION_ACTION_UP = 0x1
-
-# VehicleApPowerStateConfigFlag
-VEHICLEAPPOWERSTATECONFIGFLAG_ENABLE_DEEP_SLEEP_FLAG = 0x1
-VEHICLEAPPOWERSTATECONFIGFLAG_CONFIG_SUPPORT_TIMER_POWER_ON_FLAG = 0x2
-
-# VehicleIgnitionState
-VEHICLEIGNITIONSTATE_UNDEFINED = 0x0
-VEHICLEIGNITIONSTATE_LOCK = 0x1
-VEHICLEIGNITIONSTATE_OFF = 0x2
-VEHICLEIGNITIONSTATE_ACC = 0x3
-VEHICLEIGNITIONSTATE_ON = 0x4
-VEHICLEIGNITIONSTATE_START = 0x5
-
-# VehicleAudioVolumeLimitIndex
-VEHICLEAUDIOVOLUMELIMITINDEX_STREAM = 0x0
-VEHICLEAUDIOVOLUMELIMITINDEX_MAX_VOLUME = 0x1
-
-# VehicleAreaWindow
-VEHICLEAREAWINDOW_FRONT_WINDSHIELD = 0x1
-VEHICLEAREAWINDOW_REAR_WINDSHIELD = 0x2
-VEHICLEAREAWINDOW_ROOF_TOP = 0x4
-VEHICLEAREAWINDOW_ROW_1_LEFT = 0x10
-VEHICLEAREAWINDOW_ROW_1_RIGHT = 0x20
-VEHICLEAREAWINDOW_ROW_2_LEFT = 0x100
-VEHICLEAREAWINDOW_ROW_2_RIGHT = 0x200
-VEHICLEAREAWINDOW_ROW_3_LEFT = 0x1000
-VEHICLEAREAWINDOW_ROW_3_RIGHT = 0x2000
-
-# VehicleAudioFocusState
-VEHICLEAUDIOFOCUSSTATE_STATE_GAIN = 0x1
-VEHICLEAUDIOFOCUSSTATE_STATE_GAIN_TRANSIENT = 0x2
-VEHICLEAUDIOFOCUSSTATE_STATE_LOSS_TRANSIENT_CAN_DUCK = 0x3
-VEHICLEAUDIOFOCUSSTATE_STATE_LOSS_TRANSIENT = 0x4
-VEHICLEAUDIOFOCUSSTATE_STATE_LOSS = 0x5
-VEHICLEAUDIOFOCUSSTATE_STATE_LOSS_TRANSIENT_EXLCUSIVE = 0x6
-
-# VehicleAudioVolumeCapabilityFlag
-VEHICLEAUDIOVOLUMECAPABILITYFLAG_PERSISTENT_STORAGE = 0x1
-VEHICLEAUDIOVOLUMECAPABILITYFLAG_MASTER_VOLUME_ONLY = 0x2
-
-# VehicleApPowerState
-VEHICLEAPPOWERSTATE_OFF = 0x0
-VEHICLEAPPOWERSTATE_DEEP_SLEEP = 0x1
-VEHICLEAPPOWERSTATE_ON_DISP_OFF = 0x2
-VEHICLEAPPOWERSTATE_ON_FULL = 0x3
-VEHICLEAPPOWERSTATE_SHUTDOWN_PREPARE = 0x4
-
-# VehicleAudioVolumeState
-VEHICLEAUDIOVOLUMESTATE_STATE_OK = 0x0
-VEHICLEAUDIOVOLUMESTATE_LIMIT_REACHED = 0x1
-
-# VehicleAudioRoutingPolicyIndex
-VEHICLEAUDIOROUTINGPOLICYINDEX_STREAM = 0x0
-VEHICLEAUDIOROUTINGPOLICYINDEX_CONTEXTS = 0x1
-
-# VehicleAudioStream
-VEHICLEAUDIOSTREAM_STREAM0 = 0x0
-VEHICLEAUDIOSTREAM_STREAM1 = 0x1
-
-# VehicleInstrumentClusterType
-VEHICLEINSTRUMENTCLUSTERTYPE_NONE = 0x0
-VEHICLEINSTRUMENTCLUSTERTYPE_HAL_INTERFACE = 0x1
-VEHICLEINSTRUMENTCLUSTERTYPE_EXTERNAL_DISPLAY = 0x2
-
-# VehicleAudioExtFocusFlag
-VEHICLEAUDIOEXTFOCUSFLAG_NONE_FLAG = 0x0
-VEHICLEAUDIOEXTFOCUSFLAG_PERMANENT_FLAG = 0x1
-VEHICLEAUDIOEXTFOCUSFLAG_TRANSIENT_FLAG = 0x2
-VEHICLEAUDIOEXTFOCUSFLAG_PLAY_ONLY_FLAG = 0x4
-VEHICLEAUDIOEXTFOCUSFLAG_MUTE_MEDIA_FLAG = 0x8
-
-# VehicleHvacFanDirection
-VEHICLEHVACFANDIRECTION_FACE = 0x1
-VEHICLEHVACFANDIRECTION_FLOOR = 0x2
-VEHICLEHVACFANDIRECTION_FACE_AND_FLOOR = 0x3
-VEHICLEHVACFANDIRECTION_DEFROST = 0x4
-VEHICLEHVACFANDIRECTION_DEFROST_AND_FLOOR = 0x5
-
-# VehicleApPowerBootupReason
-VEHICLEAPPOWERBOOTUPREASON_USER_POWER_ON = 0x0
-VEHICLEAPPOWERBOOTUPREASON_USER_UNLOCK = 0x1
-VEHICLEAPPOWERBOOTUPREASON_TIMER = 0x2
-
-# VehicleArea
-VEHICLEAREA_GLOBAL = 0x1000000
-VEHICLEAREA_ZONE = 0x2000000
-VEHICLEAREA_WINDOW = 0x3000000
-VEHICLEAREA_MIRROR = 0x4000000
-VEHICLEAREA_SEAT = 0x5000000
-VEHICLEAREA_DOOR = 0x6000000
-VEHICLEAREA_MASK = 0xf000000
+# VehicleDisplay
+VEHICLEDISPLAY_MAIN = 0x0
+VEHICLEDISPLAY_INSTRUMENT_CLUSTER = 0x1
# Create a container of value_type constants to be used by vhal_emulator
class vhal_types_2_0:
diff --git a/tools/emulator/vhal_consts_2_1.py b/tools/emulator/vhal_consts_2_1.py
index d367b85..e5dbcff 100644
--- a/tools/emulator/vhal_consts_2_1.py
+++ b/tools/emulator/vhal_consts_2_1.py
@@ -15,255 +15,3 @@
# DO NOT EDIT MANUALLY
# This file was autogenerated by vhal_const_generate.py
from vhal_consts_2_0 import *
-
-# FuelType
-FUELTYPE_NOT_AVAILABLE = 0x0
-FUELTYPE_GASOLINE = 0x1
-FUELTYPE_METHANOL = 0x2
-FUELTYPE_ETHANOL = 0x3
-FUELTYPE_DIESEL = 0x4
-FUELTYPE_LPG = 0x5
-FUELTYPE_CNG = 0x6
-FUELTYPE_PROPANE = 0x7
-FUELTYPE_ELECTRIC = 0x8
-FUELTYPE_BIFUEL_RUNNING_GASOLINE = 0x9
-FUELTYPE_BIFUEL_RUNNING_METHANOL = 0xa
-FUELTYPE_BIFUEL_RUNNING_ETHANOL = 0xb
-FUELTYPE_BIFUEL_RUNNING_LPG = 0xc
-FUELTYPE_BIFUEL_RUNNING_CNG = 0xd
-FUELTYPE_BIFUEL_RUNNING_PROPANE = 0xe
-FUELTYPE_BIFUEL_RUNNING_ELECTRIC = 0xf
-FUELTYPE_BIFUEL_RUNNING_ELECTRIC_AND_COMBUSTION = 0x10
-FUELTYPE_HYBRID_GASOLINE = 0x11
-FUELTYPE_HYBRID_ETHANOL = 0x12
-FUELTYPE_HYBRID_DIESEL = 0x13
-FUELTYPE_HYBRID_ELECTRIC = 0x14
-FUELTYPE_HYBRID_RUNNING_ELECTRIC_AND_COMBUSTION = 0x15
-FUELTYPE_HYBRID_REGENERATIVE = 0x16
-FUELTYPE_BIFUEL_RUNNING_DIESEL = 0x17
-
-# VmsBaseMessageIntegerValuesIndex
-VMSBASEMESSAGEINTEGERVALUESINDEX_VMS_MESSAGE_TYPE = 0x0
-
-# SparkIgnitionMonitors
-SPARKIGNITIONMONITORS_EGR_AVAILABLE = 0x40
-SPARKIGNITIONMONITORS_EGR_INCOMPLETE = 0x80
-SPARKIGNITIONMONITORS_OXYGEN_SENSOR_HEATER_AVAILABLE = 0x100
-SPARKIGNITIONMONITORS_OXYGEN_SENSOR_HEATER_INCOMPLETE = 0x200
-SPARKIGNITIONMONITORS_OXYGEN_SENSOR_AVAILABLE = 0x400
-SPARKIGNITIONMONITORS_OXYGEN_SENSOR_INCOMPLETE = 0x800
-SPARKIGNITIONMONITORS_AC_REFRIGERANT_AVAILABLE = 0x1000
-SPARKIGNITIONMONITORS_AC_REFRIGERANT_INCOMPLETE = 0x2000
-SPARKIGNITIONMONITORS_SECONDARY_AIR_SYSTEM_AVAILABLE = 0x4000
-SPARKIGNITIONMONITORS_SECONDARY_AIR_SYSTEM_INCOMPLETE = 0x8000
-SPARKIGNITIONMONITORS_EVAPORATIVE_SYSTEM_AVAILABLE = 0x10000
-SPARKIGNITIONMONITORS_EVAPORATIVE_SYSTEM_INCOMPLETE = 0x20000
-SPARKIGNITIONMONITORS_HEATED_CATALYST_AVAILABLE = 0x40000
-SPARKIGNITIONMONITORS_HEATED_CATALYST_INCOMPLETE = 0x80000
-SPARKIGNITIONMONITORS_CATALYST_AVAILABLE = 0x100000
-SPARKIGNITIONMONITORS_CATALYST_INCOMPLETE = 0x200000
-
-# Obd2FloatSensorIndex
-OBD2FLOATSENSORINDEX_CALCULATED_ENGINE_LOAD = 0x0
-OBD2FLOATSENSORINDEX_ENGINE_COOLANT_TEMPERATURE = 0x1
-OBD2FLOATSENSORINDEX_SHORT_TERM_FUEL_TRIM_BANK1 = 0x2
-OBD2FLOATSENSORINDEX_LONG_TERM_FUEL_TRIM_BANK1 = 0x3
-OBD2FLOATSENSORINDEX_SHORT_TERM_FUEL_TRIM_BANK2 = 0x4
-OBD2FLOATSENSORINDEX_LONG_TERM_FUEL_TRIM_BANK2 = 0x5
-OBD2FLOATSENSORINDEX_FUEL_PRESSURE = 0x6
-OBD2FLOATSENSORINDEX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 0x7
-OBD2FLOATSENSORINDEX_ENGINE_RPM = 0x8
-OBD2FLOATSENSORINDEX_VEHICLE_SPEED = 0x9
-OBD2FLOATSENSORINDEX_TIMING_ADVANCE = 0xa
-OBD2FLOATSENSORINDEX_MAF_AIR_FLOW_RATE = 0xb
-OBD2FLOATSENSORINDEX_THROTTLE_POSITION = 0xc
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR1_VOLTAGE = 0xd
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR1_SHORT_TERM_FUEL_TRIM = 0xe
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR1_FUEL_AIR_EQUIVALENCE_RATIO = 0xf
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR2_VOLTAGE = 0x10
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR2_SHORT_TERM_FUEL_TRIM = 0x11
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR2_FUEL_AIR_EQUIVALENCE_RATIO = 0x12
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR3_VOLTAGE = 0x13
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR3_SHORT_TERM_FUEL_TRIM = 0x14
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR3_FUEL_AIR_EQUIVALENCE_RATIO = 0x15
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR4_VOLTAGE = 0x16
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR4_SHORT_TERM_FUEL_TRIM = 0x17
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR4_FUEL_AIR_EQUIVALENCE_RATIO = 0x18
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR5_VOLTAGE = 0x19
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR5_SHORT_TERM_FUEL_TRIM = 0x1a
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR5_FUEL_AIR_EQUIVALENCE_RATIO = 0x1b
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR6_VOLTAGE = 0x1c
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR6_SHORT_TERM_FUEL_TRIM = 0x1d
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR6_FUEL_AIR_EQUIVALENCE_RATIO = 0x1e
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR7_VOLTAGE = 0x1f
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR7_SHORT_TERM_FUEL_TRIM = 0x20
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR7_FUEL_AIR_EQUIVALENCE_RATIO = 0x21
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR8_VOLTAGE = 0x22
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR8_SHORT_TERM_FUEL_TRIM = 0x23
-OBD2FLOATSENSORINDEX_OXYGEN_SENSOR8_FUEL_AIR_EQUIVALENCE_RATIO = 0x24
-OBD2FLOATSENSORINDEX_FUEL_RAIL_PRESSURE = 0x25
-OBD2FLOATSENSORINDEX_FUEL_RAIL_GAUGE_PRESSURE = 0x26
-OBD2FLOATSENSORINDEX_COMMANDED_EXHAUST_GAS_RECIRCULATION = 0x27
-OBD2FLOATSENSORINDEX_EXHAUST_GAS_RECIRCULATION_ERROR = 0x28
-OBD2FLOATSENSORINDEX_COMMANDED_EVAPORATIVE_PURGE = 0x29
-OBD2FLOATSENSORINDEX_FUEL_TANK_LEVEL_INPUT = 0x2a
-OBD2FLOATSENSORINDEX_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 0x2b
-OBD2FLOATSENSORINDEX_CATALYST_TEMPERATURE_BANK1_SENSOR1 = 0x2c
-OBD2FLOATSENSORINDEX_CATALYST_TEMPERATURE_BANK2_SENSOR1 = 0x2d
-OBD2FLOATSENSORINDEX_CATALYST_TEMPERATURE_BANK1_SENSOR2 = 0x2e
-OBD2FLOATSENSORINDEX_CATALYST_TEMPERATURE_BANK2_SENSOR2 = 0x2f
-OBD2FLOATSENSORINDEX_ABSOLUTE_LOAD_VALUE = 0x30
-OBD2FLOATSENSORINDEX_FUEL_AIR_COMMANDED_EQUIVALENCE_RATIO = 0x31
-OBD2FLOATSENSORINDEX_RELATIVE_THROTTLE_POSITION = 0x32
-OBD2FLOATSENSORINDEX_ABSOLUTE_THROTTLE_POSITION_B = 0x33
-OBD2FLOATSENSORINDEX_ABSOLUTE_THROTTLE_POSITION_C = 0x34
-OBD2FLOATSENSORINDEX_ACCELERATOR_PEDAL_POSITION_D = 0x35
-OBD2FLOATSENSORINDEX_ACCELERATOR_PEDAL_POSITION_E = 0x36
-OBD2FLOATSENSORINDEX_ACCELERATOR_PEDAL_POSITION_F = 0x37
-OBD2FLOATSENSORINDEX_COMMANDED_THROTTLE_ACTUATOR = 0x38
-OBD2FLOATSENSORINDEX_ETHANOL_FUEL_PERCENTAGE = 0x39
-OBD2FLOATSENSORINDEX_ABSOLUTE_EVAPORATION_SYSTEM_VAPOR_PRESSURE = 0x3a
-OBD2FLOATSENSORINDEX_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 0x3b
-OBD2FLOATSENSORINDEX_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 0x3c
-OBD2FLOATSENSORINDEX_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 0x3d
-OBD2FLOATSENSORINDEX_SHORT_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 0x3e
-OBD2FLOATSENSORINDEX_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK1 = 0x3f
-OBD2FLOATSENSORINDEX_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK2 = 0x40
-OBD2FLOATSENSORINDEX_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK3 = 0x41
-OBD2FLOATSENSORINDEX_LONG_TERM_SECONDARY_OXYGEN_SENSOR_TRIM_BANK4 = 0x42
-OBD2FLOATSENSORINDEX_RELATIVE_ACCELERATOR_PEDAL_POSITION = 0x43
-OBD2FLOATSENSORINDEX_HYBRID_BATTERY_PACK_REMAINING_LIFE = 0x44
-OBD2FLOATSENSORINDEX_FUEL_INJECTION_TIMING = 0x45
-OBD2FLOATSENSORINDEX_ENGINE_FUEL_RATE = 0x46
-OBD2FLOATSENSORINDEX_LAST_SYSTEM_INDEX = 0x46
-
-# CommonIgnitionMonitors
-COMMONIGNITIONMONITORS_COMPONENTS_AVAILABLE = 0x1
-COMMONIGNITIONMONITORS_COMPONENTS_INCOMPLETE = 0x2
-COMMONIGNITIONMONITORS_FUEL_SYSTEM_AVAILABLE = 0x4
-COMMONIGNITIONMONITORS_FUEL_SYSTEM_INCOMPLETE = 0x8
-COMMONIGNITIONMONITORS_MISFIRE_AVAILABLE = 0x10
-COMMONIGNITIONMONITORS_MISFIRE_INCOMPLETE = 0x20
-
-# IgnitionMonitorKind
-IGNITIONMONITORKIND_SPARK = 0x0
-IGNITIONMONITORKIND_COMPRESSION = 0x1
-
-# SecondaryAirStatus
-SECONDARYAIRSTATUS_UPSTREAM = 0x1
-SECONDARYAIRSTATUS_DOWNSTREAM_OF_CATALYCIC_CONVERTER = 0x2
-SECONDARYAIRSTATUS_FROM_OUTSIDE_OR_OFF = 0x4
-SECONDARYAIRSTATUS_PUMP_ON_FOR_DIAGNOSTICS = 0x8
-
-# VmsMessageType
-VMSMESSAGETYPE_SUBSCRIBE = 0x1
-VMSMESSAGETYPE_UNSUBSCRIBE = 0x2
-VMSMESSAGETYPE_DATA = 0x3
-VMSMESSAGETYPE_OFFERING = 0x4
-VMSMESSAGETYPE_AVAILABILITY_REQUEST = 0x5
-VMSMESSAGETYPE_AVAILABILITY_RESPONSE = 0x6
-VMSMESSAGETYPE_SUBSCRIPTION_REQUEST = 0x7
-VMSMESSAGETYPE_SUBSCRIPTION_RESPONSE = 0x8
-
-# CompressionIgnitionMonitors
-COMPRESSIONIGNITIONMONITORS_EGR_OR_VVT_AVAILABLE = 0x40
-COMPRESSIONIGNITIONMONITORS_EGR_OR_VVT_INCOMPLETE = 0x80
-COMPRESSIONIGNITIONMONITORS_PM_FILTER_AVAILABLE = 0x100
-COMPRESSIONIGNITIONMONITORS_PM_FILTER_INCOMPLETE = 0x200
-COMPRESSIONIGNITIONMONITORS_EXHAUST_GAS_SENSOR_AVAILABLE = 0x400
-COMPRESSIONIGNITIONMONITORS_EXHAUST_GAS_SENSOR_INCOMPLETE = 0x800
-COMPRESSIONIGNITIONMONITORS_BOOST_PRESSURE_AVAILABLE = 0x1000
-COMPRESSIONIGNITIONMONITORS_BOOST_PRESSURE_INCOMPLETE = 0x2000
-COMPRESSIONIGNITIONMONITORS_NOx_SCR__AVAILABLE = 0x4000
-COMPRESSIONIGNITIONMONITORS_NOx_SCR_INCOMPLETE = 0x8000
-COMPRESSIONIGNITIONMONITORS_NMHC_CATALYST_AVAILABLE = 0x10000
-COMPRESSIONIGNITIONMONITORS_NMHC_CATALYST_INCOMPLETE = 0x20000
-
-# VehiclePropertyGroup
-VEHICLEPROPERTYGROUP_SYSTEM = 0x10000000
-VEHICLEPROPERTYGROUP_VENDOR = 0x20000000
-VEHICLEPROPERTYGROUP_MASK = 0xf0000000
-
-# Obd2IntegerSensorIndex
-OBD2INTEGERSENSORINDEX_FUEL_SYSTEM_STATUS = 0x0
-OBD2INTEGERSENSORINDEX_MALFUNCTION_INDICATOR_LIGHT_ON = 0x1
-OBD2INTEGERSENSORINDEX_IGNITION_MONITORS_SUPPORTED = 0x2
-OBD2INTEGERSENSORINDEX_IGNITION_SPECIFIC_MONITORS = 0x3
-OBD2INTEGERSENSORINDEX_INTAKE_AIR_TEMPERATURE = 0x4
-OBD2INTEGERSENSORINDEX_COMMANDED_SECONDARY_AIR_STATUS = 0x5
-OBD2INTEGERSENSORINDEX_NUM_OXYGEN_SENSORS_PRESENT = 0x6
-OBD2INTEGERSENSORINDEX_RUNTIME_SINCE_ENGINE_START = 0x7
-OBD2INTEGERSENSORINDEX_DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON = 0x8
-OBD2INTEGERSENSORINDEX_WARMUPS_SINCE_CODES_CLEARED = 0x9
-OBD2INTEGERSENSORINDEX_DISTANCE_TRAVELED_SINCE_CODES_CLEARED = 0xa
-OBD2INTEGERSENSORINDEX_ABSOLUTE_BAROMETRIC_PRESSURE = 0xb
-OBD2INTEGERSENSORINDEX_CONTROL_MODULE_VOLTAGE = 0xc
-OBD2INTEGERSENSORINDEX_AMBIENT_AIR_TEMPERATURE = 0xd
-OBD2INTEGERSENSORINDEX_TIME_WITH_MALFUNCTION_LIGHT_ON = 0xe
-OBD2INTEGERSENSORINDEX_TIME_SINCE_TROUBLE_CODES_CLEARED = 0xf
-OBD2INTEGERSENSORINDEX_MAX_FUEL_AIR_EQUIVALENCE_RATIO = 0x10
-OBD2INTEGERSENSORINDEX_MAX_OXYGEN_SENSOR_VOLTAGE = 0x11
-OBD2INTEGERSENSORINDEX_MAX_OXYGEN_SENSOR_CURRENT = 0x12
-OBD2INTEGERSENSORINDEX_MAX_INTAKE_MANIFOLD_ABSOLUTE_PRESSURE = 0x13
-OBD2INTEGERSENSORINDEX_MAX_AIR_FLOW_RATE_FROM_MASS_AIR_FLOW_SENSOR = 0x14
-OBD2INTEGERSENSORINDEX_FUEL_TYPE = 0x15
-OBD2INTEGERSENSORINDEX_FUEL_RAIL_ABSOLUTE_PRESSURE = 0x16
-OBD2INTEGERSENSORINDEX_ENGINE_OIL_TEMPERATURE = 0x17
-OBD2INTEGERSENSORINDEX_DRIVER_DEMAND_PERCENT_TORQUE = 0x18
-OBD2INTEGERSENSORINDEX_ENGINE_ACTUAL_PERCENT_TORQUE = 0x19
-OBD2INTEGERSENSORINDEX_ENGINE_REFERENCE_PERCENT_TORQUE = 0x1a
-OBD2INTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_IDLE = 0x1b
-OBD2INTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_POINT1 = 0x1c
-OBD2INTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_POINT2 = 0x1d
-OBD2INTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_POINT3 = 0x1e
-OBD2INTEGERSENSORINDEX_ENGINE_PERCENT_TORQUE_DATA_POINT4 = 0x1f
-OBD2INTEGERSENSORINDEX_LAST_SYSTEM_INDEX = 0x1f
-
-# VehicleProperty
-VEHICLEPROPERTY_WHEEL_TICK = 0x11610306
-VEHICLEPROPERTY_OBD2_LIVE_FRAME = 0x11e00d00
-VEHICLEPROPERTY_OBD2_FREEZE_FRAME = 0x11e00d01
-VEHICLEPROPERTY_OBD2_FREEZE_FRAME_INFO = 0x11e00d02
-VEHICLEPROPERTY_OBD2_FREEZE_FRAME_CLEAR = 0x11e00d03
-VEHICLEPROPERTY_VEHICLE_MAP_SERVICE = 0x11e00c00
-
-# FuelSystemStatus
-FUELSYSTEMSTATUS_OPEN_INSUFFICIENT_ENGINE_TEMPERATURE = 0x1
-FUELSYSTEMSTATUS_CLOSED_LOOP = 0x2
-FUELSYSTEMSTATUS_OPEN_ENGINE_LOAD_OR_DECELERATION = 0x4
-FUELSYSTEMSTATUS_OPEN_SYSTEM_FAILURE = 0x8
-FUELSYSTEMSTATUS_CLOSED_LOOP_BUT_FEEDBACK_FAULT = 0x10
-
-# VehiclePropertyType
-VEHICLEPROPERTYTYPE_STRING = 0x100000
-VEHICLEPROPERTYTYPE_BOOLEAN = 0x200000
-VEHICLEPROPERTYTYPE_INT32 = 0x400000
-VEHICLEPROPERTYTYPE_INT32_VEC = 0x410000
-VEHICLEPROPERTYTYPE_INT64 = 0x500000
-VEHICLEPROPERTYTYPE_FLOAT = 0x600000
-VEHICLEPROPERTYTYPE_FLOAT_VEC = 0x610000
-VEHICLEPROPERTYTYPE_BYTES = 0x700000
-VEHICLEPROPERTYTYPE_COMPLEX = 0xe00000
-VEHICLEPROPERTYTYPE_MASK = 0xff0000
-
-# VmsSimpleMessageIntegerValuesIndex
-VMSSIMPLEMESSAGEINTEGERVALUESINDEX_VMS_LAYER_ID = 0x1
-VMSSIMPLEMESSAGEINTEGERVALUESINDEX_VMS_LAYER_VERSION = 0x2
-
-# VehicleArea
-VEHICLEAREA_GLOBAL = 0x1000000
-VEHICLEAREA_ZONE = 0x2000000
-VEHICLEAREA_WINDOW = 0x3000000
-VEHICLEAREA_MIRROR = 0x4000000
-VEHICLEAREA_SEAT = 0x5000000
-VEHICLEAREA_DOOR = 0x6000000
-VEHICLEAREA_MASK = 0xf000000
-
-# VmsOfferingMessageIntegerValuesIndex
-VMSOFFERINGMESSAGEINTEGERVALUESINDEX_VMS_NUMBER_OF_LAYERS_DEPENDENCIES = 0x1
-VMSOFFERINGMESSAGEINTEGERVALUESINDEX_FIRST_DEPENDENCIES_INDEX = 0x2
-
-# VmsSubscriptionResponseFormat
-VMSSUBSCRIPTIONRESPONSEFORMAT_SEQUENCE_NUMBER = 0x1
-VMSSUBSCRIPTIONRESPONSEFORMAT_NUMBER_OF_LAYERS = 0x2
-VMSSUBSCRIPTIONRESPONSEFORMAT_FIRST_LAYER = 0x3
diff --git a/tools/emulator/vhal_emulator.py b/tools/emulator/vhal_emulator.py
index 4c94e4f..7cc6da7 100644
--- a/tools/emulator/vhal_emulator.py
+++ b/tools/emulator/vhal_emulator.py
@@ -129,17 +129,22 @@
"""
print("len = ", len(data), "str = ", ":".join("{:02x}".format(ord(d)) for d in data))
- def openSocket(self):
+ def openSocket(self, device=None):
"""
Connects to an Android Auto device running a Vehicle HAL with simulator.
"""
# Hard-coded socket port needs to match the one in DefaultVehicleHal
- portNumber = 33452
- # Setup ADB port forwarding
- subprocess.call("adb forward tcp:%d tcp:%d" % (portNumber, portNumber), shell=True)
+ remotePortNumber = 33452
+ extraArgs = '' if device is None else '-s %s' % device
+ adbCmd = 'adb %s forward tcp:0 tcp:%d' % (extraArgs, remotePortNumber)
+ adbResp = subprocess.check_output(adbCmd, shell=True)[0:-1]
+ localPortNumber = int(adbResp)
+ print('Connecting local port %s to remote port %s on %s' % (
+ localPortNumber, remotePortNumber,
+ 'default device' if device is None else 'device %s' % device))
# Open the socket and connect
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- self.sock.connect(('localhost', portNumber))
+ self.sock.connect(('localhost', localPortNumber))
def rxMsg(self):
"""
@@ -249,11 +254,11 @@
return
self._txCmd(cmd)
- def __init__(self, types):
+ def __init__(self, types, device=None):
# Save the list of types constants
self._types = types
# Open the socket
- self.openSocket()
+ self.openSocket(device)
# Get the list of configs
self.getConfigAll()
msg = self.rxMsg()
diff --git a/tools/emulator/vhal_emulator_test.py b/tools/emulator/vhal_emulator_test.py
index 1d0ada0..ea4030d 100755
--- a/tools/emulator/vhal_emulator_test.py
+++ b/tools/emulator/vhal_emulator_test.py
@@ -37,7 +37,7 @@
sys.dont_write_bytecode = True
import VehicleHalProto_pb2
-import vhal_consts_2_0
+import vhal_consts_2_1
import vhal_emulator
import logging
@@ -47,6 +47,16 @@
_configs = 0 # List of configs from DUT
_log = 0 # Logger module
_vhal = 0 # Handle to VHAL object that communicates over socket to DUT
+ # TODO: b/38203109 - Fix OBD2 values, implement handling for complex properties
+ _skipProps = [
+ vhal_consts_2_1.VEHICLEPROPERTY_OBD2_LIVE_FRAME,
+ vhal_consts_2_1.VEHICLEPROPERTY_OBD2_FREEZE_FRAME,
+ vhal_consts_2_1.VEHICLEPROPERTY_OBD2_FREEZE_FRAME_INFO,
+ vhal_consts_2_1.VEHICLEPROPERTY_OBD2_FREEZE_FRAME_CLEAR,
+ vhal_consts_2_1.VEHICLEPROPERTY_VEHICLE_MAP_SERVICE,
+ vhal_consts_2_1.VEHICLEPROPERTY_WHEEL_TICK, # Need to support complex properties
+ 0x21E00666 # FakeDataControllingProperty - an internal test property
+ ]
def _getMidpoint(self, minVal, maxVal):
retVal = minVal + (maxVal - minVal)/2
@@ -60,7 +70,7 @@
elif valType in self._types.TYPE_BYTES:
# Generate array of integers counting from 0
testValue = list(range(len(origValue)))
- elif valType == vhal_consts_2_0.VEHICLEPROPERTYTYPE_BOOLEAN:
+ elif valType == vhal_consts_2_1.VEHICLEPROPERTYTYPE_BOOLEAN:
testValue = origValue ^ 1
elif valType in self._types.TYPE_INT32:
try:
@@ -87,7 +97,7 @@
testValue = "%.5f" % testValue
testValue = float(testValue)
else:
- self._log.error("generateTestValue: valType=%d is not handled", valType)
+ self._log.error("generateTestValue: valType=0x%X is not handled", valType)
testValue = None
return testValue
@@ -103,7 +113,7 @@
value = rxMsg.value[0].string_value
elif valType in self._types.TYPE_BYTES:
value = rxMsg.value[0].bytes_value
- elif valType == vhal_consts_2_0.VEHICLEPROPERTYTYPE_BOOLEAN:
+ elif valType == vhal_consts_2_1.VEHICLEPROPERTYTYPE_BOOLEAN:
value = rxMsg.value[0].int32_values[0]
elif valType in self._types.TYPE_INT32:
value = rxMsg.value[0].int32_values[0]
@@ -115,7 +125,7 @@
value = "%.5f" % value
value = float(value)
else:
- self._log.error("getValuesFromMsg: valType=%d is not handled", valType)
+ self._log.error("getValueFromMsg: valType=0x%X is not handled", valType)
value = None
return value
@@ -126,10 +136,10 @@
retVal = 1
rxMsg = self._vhal.rxMsg()
if rxMsg.msg_type != expectedType:
- self._log.error("rxMsg Type expected: %d, received: %d", expectedType, rxMsg.msg_type)
+ self._log.error("rxMsg Type expected: 0x%X, received: 0x%X", expectedType, rxMsg.msg_type)
retVal = 0
if rxMsg.status != expectedStatus:
- self._log.error("rxMsg Status expected: %d, received: %d", expectedStatus, rxMsg.status)
+ self._log.error("rxMsg Status expected: 0x%X, received: 0x%X", expectedStatus, rxMsg.status)
retVal = 0
return rxMsg, retVal
@@ -138,13 +148,13 @@
def testGetConfig(self):
self._log.info("Starting testGetConfig...")
for cfg in self._configs:
- self._log.debug(" Getting config for propId=%d", cfg.prop)
+ self._log.debug(" Getting config for propId=0x%X", cfg.prop)
self._vhal.getConfig(cfg.prop)
rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_CONFIG_RESP,
VehicleHalProto_pb2.RESULT_OK)
if retVal:
if rxMsg.config[0] != cfg:
- self._log.error("testGetConfig failed. prop=%d, expected:\n%s\nreceived:\n%s",
+ self._log.error("testGetConfig failed. prop=0x%X, expected:\n%s\nreceived:\n%s",
cfg.prop, str(cfg), str(rxMsg.config))
self._log.info(" Finished testGetConfig!")
@@ -152,13 +162,13 @@
def testGetBadConfig(self):
self._log.info("Starting testGetBadConfig...")
for prop in self._badProps:
- self._log.debug(" Testing bad propId=%d", prop)
+ self._log.debug(" Testing bad propId=0x%X", prop)
self._vhal.getConfig(prop)
rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_CONFIG_RESP,
VehicleHalProto_pb2.ERROR_INVALID_PROPERTY)
if retVal:
for cfg in rxMsg.config:
- self._log.error("testGetBadConfig prop=%d, expected:None, received:\n%s",
+ self._log.error("testGetBadConfig prop=0x%X, expected:None, received:\n%s",
cfg.prop, str(rxMsg.config))
self._log.info(" Finished testGetBadConfig!")
@@ -177,6 +187,11 @@
def testGetSet(self):
self._log.info("Starting testGetSet()...")
for cfg in self._configs:
+ if cfg.prop in self._skipProps:
+ # Skip properties that cannot be handled properly by this test.
+ self._log.warning(" Skipping propId=0x%X", cfg.prop)
+ continue
+
areas = cfg.supported_areas
idx = -1
while (idx == -1) | (areas != 0):
@@ -188,7 +203,7 @@
# Remove the area from areas
areas ^= area
- self._log.debug(" Testing propId=%d, area=%d", cfg.prop, area)
+ self._log.debug(" Testing propId=0x%X, area=0x%X", cfg.prop, area)
# Get the current value
self._vhal.getProperty(cfg.prop, area)
@@ -198,21 +213,21 @@
# Save the original value
origValue = self._getValueFromMsg(rxMsg)
if origValue == None:
- self._log.error("testGetSet: Could not get value for prop=%d, area=%d",
+ self._log.error("testGetSet: Could not get value for prop=0x%X, area=0x%X",
cfg.prop, area)
continue
# Generate the test value
testValue = self._generateTestValue(cfg, idx, origValue)
if testValue == None:
- self._log.error("testGetSet: Cannot generate test value for prop=%d, area=%d",
+ self._log.error("testGetSet: Cannot generate test value for prop=0x%X, area=0x%X",
cfg.prop, area)
continue
# Send the new value
self._vhal.setProperty(cfg.prop, area, testValue)
rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.SET_PROPERTY_RESP,
- VehicleHalProto_pb2.RESULT_OK)
+ VehicleHalProto_pb2.RESULT_OK)
# Get the new value and verify it
self._vhal.getProperty(cfg.prop, area)
@@ -220,7 +235,7 @@
VehicleHalProto_pb2.RESULT_OK)
newValue = self._getValueFromMsg(rxMsg)
if newValue != testValue:
- self._log.error("testGetSet: set failed for propId=%d, area=%d", cfg.prop, area)
+ self._log.error("testGetSet: set failed for propId=0x%X, area=0x%X", cfg.prop, area)
print("testValue= ", testValue, "newValue= ", newValue)
continue
@@ -233,13 +248,13 @@
def testGetBadProperty(self):
self._log.info("Starting testGetBadProperty()...")
for prop in self._badProps:
- self._log.debug(" Testing bad propId=%d", prop)
+ self._log.debug(" Testing bad propId=0x%X", prop)
self._vhal.getProperty(prop, 0)
rxMsg, retVal = self._rxMsgAndValidate(VehicleHalProto_pb2.GET_PROPERTY_RESP,
VehicleHalProto_pb2.ERROR_INVALID_PROPERTY)
if retVal:
for value in rxMsg.value:
- self._log.error("testGetBadProperty prop=%d, expected:None, received:\n%s",
+ self._log.error("testGetBadProperty prop=0x%X, expected:None, received:\n%s",
prop, str(rxMsg))
self._log.info(" Finished testGetBadProperty()!")
@@ -248,12 +263,12 @@
area = 1
value = 100
for prop in self._badProps:
- self._log.debug(" Testing bad propId=%d", prop)
+ self._log.debug(" Testing bad propId=0x%X", prop)
area = area + 1
value = value + 1
try:
self._vhal.setProperty(prop, area, value)
- self._log.error("testGetBadProperty failed. prop=%d, area=%d, value=%d",
+ self._log.error("testGetBadProperty failed. prop=0x%X, area=0x%X, value=%d",
prop, area, value)
except ValueError as e:
# Received expected error
@@ -290,5 +305,5 @@
self._configs = self._vhal.rxMsg().config
if __name__ == '__main__':
- v = VhalTest(vhal_consts_2_0.vhal_types_2_0)
+ v = VhalTest(vhal_consts_2_1.vhal_types_2_0)
v.runTests()
diff --git a/tools/io_analysis/check_file_read.py b/tools/io_analysis/check_file_read.py
index 06b4f43..f785436 100644
--- a/tools/io_analysis/check_file_read.py
+++ b/tools/io_analysis/check_file_read.py
@@ -23,6 +23,7 @@
import sys
DBG = False
+DBG_ISSUE = False
# hard coded maps to detect partition for given device or the other way around
# this can be different per each device. This works for marlin.
@@ -31,83 +32,240 @@
for key, value in DEVICE_TO_PARTITION.iteritems():
PARTITION_TO_DEVICE[value] = key
-RE_DO_SYS_OPEN = r""".+\s+([0-9]+\.[0-9]+):\s+do_sys_open:\s+(\S+):\sopen..(\S+).,\s([0-9]+).\s+.+inode\s=\s([0-9]+)"""
+# init-1 [003] .... 2.703964: do_sys_open: init: open("/sys/fs/selinux/null", 131074, 0) fd = 0, inode = 22
+RE_DO_SYS_OPEN = r"""\s+\S+-([0-9]+).*\s+([0-9]+\.[0-9]+):\s+do_sys_open:\s+(\S+):\sopen..(\S+).,\s([0-9]+).\s+.+inode\s=\s([0-9]+)"""
+# init-1 [003] ...1 2.703991: ext4_ext_map_blocks_enter: dev 253,0 ino 2719 lblk 154 len 30 flags
RE_EXT4_MA_BLOCKS_ENTER = r"""\s+(\S+)-([0-9]+).+\s+([0-9]+\.[0-9]+):\s+ext4_ext_map_blocks_enter:\s+dev\s+(\S+)\s+ino\s+([0-9]+)\s+lblk\s+([0-9]+)\s+len\s+([0-9]+)"""
+# init-1 [002] ...1 2.687205: ext4_ext_map_blocks_exit: dev 253,0 ino 8 flags lblk 0 pblk 196608 len 1 mflags M ret 1
+RE_EXT4_MA_BLOCKS_EXIT = r"""\s+(\S+)-([0-9]+).+\s+([0-9]+\.[0-9]+):\s+ext4_ext_map_blocks_exit:\s+dev\s+(\S+)\s+ino\s+([0-9]+)\sflags.*\slblk\s+([0-9]+)\spblk\s([0-9]+)\slen\s+([0-9]+).*mflags\s(\S*)\sret\s([0-9]+)"""
+# init-1 [002] ...1 2.887119: block_bio_remap: 8,0 R 10010384 + 8 <- (259,18) 3998944
+RE_BLOCK_BIO_REMAP = r""".+block_bio_remap:\s\d+,\d+\s\S+\s(\d+)\s\+\s\d+\s<-\s\([^\)]+\)\s(\d+)"""
+# kworker/u9:1-83 [003] d..2 2.682991: block_rq_issue: 8,0 RA 0 () 10140208 + 32 [kworker/u9:1]
+RE_BLOCK_RQ_ISSUE = r"""\s+\S+-([0-9]+).*\s+([0-9]+\.[0-9]+):\s+block_rq_issue:\s+([0-9]+)\,([0-9]+)\s+([RW]\S*)\s[0-9]+\s\([^\)]*\)\s([0-9]+)\s+\+\s+([0-9]+)\s+\[([^\]]+)\]"""
+
+EXT4_SIZE_TO_BLOCK_SIZE = 8 # ext4: 4KB, block device block size: 512B
+
+class FileAccess:
+ def __init__(self, file):
+ self.file = file
+ self.accesses = []
+ self.total_access = 0
+ self.ext4_access_size_histogram = {} #key: read size, value: occurrence
+ self.block_access_size_histogram = {}
+ self.ext4_single_block_accesses = {} # process name, occurrence
+ self.block_single_block_accesses = {} # process name, occurrence
+ self.blocks_histogram = {} # K: offset, V: read counter
+
+ def add_if_single_block(self, container, size, offset, process_name):
+ if size != 1:
+ return
+ offsets = container.get(process_name)
+ if not offsets:
+ offsets = []
+ container[process_name] = offsets
+ offsets.append(offset)
+
+ def add_access(self, time, offset, size, process_name, read_sizes):
+ self.accesses.append((time, offset, size, process_name))
+ self.total_access += size
+ self.ext4_access_size_histogram[size] = self.ext4_access_size_histogram.get(size, 0) + 1
+ read_offset = offset
+ for s in read_sizes:
+ self.block_access_size_histogram[s] = self.block_access_size_histogram.get(s, 0) + 1
+ self.add_if_single_block(self.block_single_block_accesses, s, read_offset, process_name)
+ read_offset += s
+ for i in range(size):
+ self.blocks_histogram[offset + i] = self.blocks_histogram.get(offset + i, 0) + 1
+ self.add_if_single_block(self.ext4_single_block_accesses, size, offset, process_name)
+
+ def add_merged_access(self, time, offsets, lens, process_names):
+ total_read_offsets = set() # each read can overwrap. So count only once for block level counting
+ for i in range(len(offsets)):
+ self.accesses.append((time, offsets[i], lens[i], process_names[i]))
+ self.ext4_access_size_histogram[lens[i]] = self.ext4_access_size_histogram.get(lens[i], 0) + 1
+ self.add_if_single_block(self.ext4_single_block_accesses, lens[i], offsets[i], process_names[i])
+ for j in range(len(lens)):
+ total_read_offsets.add(offsets[i] + j)
+ total_lens = len(total_read_offsets)
+ start_offset = min(total_read_offsets)
+ self.total_access += total_lens
+ self.block_access_size_histogram[total_lens] = self.block_access_size_histogram.get(total_lens, 0) \
+ + 1
+ self.add_if_single_block(self.block_single_block_accesses, total_lens, start_offset, \
+ process_names[0])
+ for s in range(total_lens):
+ self.blocks_histogram[start_offset + s] = self.blocks_histogram.get(start_offset + s, 0) + 1
+
+
+ def dump(self):
+ if len(self.ext4_access_size_histogram) > 1:
+ print " Ext4 access size histograms:", collections.OrderedDict( \
+ sorted(self.ext4_access_size_histogram.items(), key = lambda item: item[0]))
+ if len(self.ext4_single_block_accesses) > 0 and self.total_access > 1:
+ print " Ext4 single block accesses:", collections.OrderedDict( \
+ sorted(self.ext4_single_block_accesses.items(), key = lambda item: item[1], reverse = True))
+ if len(self.block_access_size_histogram) > 1:
+ print " Block access size histograms:", collections.OrderedDict( \
+ sorted(self.block_access_size_histogram.items(), key = lambda item: item[0]))
+ if len(self.block_single_block_accesses) > 0 and self.total_access > 1:
+ print " Block single block accesses:", collections.OrderedDict( \
+ sorted(self.block_single_block_accesses.items(), key = lambda item: item[1], reverse = True))
+ if self.total_access > 1:
+ sorted_blocks_histogram = sorted(self.blocks_histogram.items(), key = lambda item: item[1], \
+ reverse = True)
+ prints = []
+ repeating_reads_counter = 0
+ for entry in sorted_blocks_histogram:
+ offset = entry[0]
+ counter = entry[1]
+ if counter == 1:
+ break
+ prints.append(str(offset) + ":" + str(counter))
+ repeating_reads_counter += (counter - 1)
+ if len(prints) > 0:
+ print " repeating accesses", repeating_reads_counter, " offset:count ->", ','.join(prints)
class FileEvent:
def __init__(self, open_time, file_name, process_name, inode, flags):
self.file_name = file_name
self.inode = inode
+ self.total_open = 1
self.processes = []
self.processes.append((open_time, process_name, flags))
- self.reads = []
- self.total_reads = 0
- self.total_open = 1
- self.blocks = {}
- self.total_rereads = 0
- self.read_size_histogram = {} #key: read size, value: occurrence
- self.single_block_reads = {} # process name, occurrence
+ self.read = FileAccess(self)
+ self.write = FileAccess(self)
+
def add_open(self, open_time, process_name, flags):
self.processes.append((open_time, process_name, flags))
self.total_open += 1
- def add_read(self, time, offset, size, process_name):
- self.reads.append((time, offset, size, process_name))
- self.total_reads += size
- for i in range(offset, offset + size):
- if not self.blocks.get(i):
- self.blocks[i] = 1
- else:
- self.blocks[i] += 1
- self.total_rereads += 1
- if not self.read_size_histogram.get(size):
- self.read_size_histogram[size] = 1
+ def add_access(self, is_read, time, offset, size, process_name, read_sizes):
+ if is_read:
+ self.read.add_access(time, offset, size, process_name, read_sizes)
else:
- self.read_size_histogram[size] += 1
- if size == 1:
- if not self.single_block_reads.get(process_name):
- self.single_block_reads[process_name] = 1
- else:
- self.single_block_reads[process_name] += 1
+ self.write.add_access(time, offset, size, process_name, read_sizes)
- def dump(self):
- print " filename %s, total reads %d, total open %d total rereads %d inode %s" \
- % (self.file_name, self.total_reads, self.total_open, self.total_rereads, self.inode)
+ def add_merged_access(self, is_read, time, offsets, lens, process_names):
+ if is_read:
+ self.read.add_merged_access(time, offsets, lens, process_names)
+ else:
+ self.write.add_merged_access(time, offsets, lens, process_names)
+
+ def dump(self, name_to_pid_map):
+ print " ***filename %s, total reads %d, total writes %d, total open %d inode %s" \
+ % (self.file_name, self.read.total_access, self.write.total_access, self.total_open,\
+ self.inode)
process_names = []
for opener in self.processes:
- process_names.append(opener[1])
+ process_names.append(opener[1] + "-" + name_to_pid_map.get(opener[1], '?') + " t:" + \
+ str(opener[0]) + " flags:" + opener[2])
print " Processes opened this file:", ','.join(process_names)
- if len(self.read_size_histogram) > 1:
- print " Read size histograms:", collections.OrderedDict( \
- sorted(self.read_size_histogram.items(), key = lambda item: item[0]))
- if len(self.single_block_reads) > 1 and len(self.reads) > 1:
- print " Single block reads:", collections.OrderedDict( \
- sorted(self.single_block_reads.items(), key = lambda item: item[1], reverse = True))
+ if self.read.total_access > 0:
+ print " ****Reads:"
+ self.read.dump()
+ if self.write.total_access > 0:
+ print " ****Writes:"
+ self.write.dump()
+
+ def dump_short(self):
+ print " filename %s, total reads %d, total writes %d" % (self.file_name,
+ self.read.total_access, self.write.total_access)
+
+class PendingAccess:
+ def __init__(self, process_name, pid, time, dev, inode, lblk, pblk, len, fevent):
+ self.process_name = process_name
+ self.pid = pid
+ self.time = time
+ self.dev = dev
+ self.inode = inode
+ self.lblk = lblk
+ self.pblk = pblk
+ self.blk_len = len * EXT4_SIZE_TO_BLOCK_SIZE
+ self.len = len
+ self.fevent = fevent
+ self.pending_accesses = set()
+ for i in range(len):
+ self.pending_accesses.add(i)
+ self.access_sizes = [] # valid read for this file in block dev level.
+ self.block_access_counter = 0
+
+ def get_valid_access(self, block_offset, block_len):
+ ext4_offset = block_offset / EXT4_SIZE_TO_BLOCK_SIZE
+ if ext4_offset > self.len:
+ return 0, 0
+ ext4_len = block_len / EXT4_SIZE_TO_BLOCK_SIZE
+ if (ext4_offset + ext4_len) > self.len:
+ ext4_len = self.len - ext4_offset
+ return ext4_offset, ext4_len
+
+ def queue_block_access(self, ext4_offset, ext4_len):
+ if ext4_len <= 0:
+ return
+ self.block_access_counter += 1
+ ext4_blocks_accessed = 0
+ for i in range(ext4_len):
+ ext4_block_i = i + ext4_offset
+ if ext4_block_i in self.pending_accesses:
+ ext4_blocks_accessed += 1
+ self.pending_accesses.remove(ext4_block_i)
+ if ext4_blocks_accessed > 0:
+ self.access_sizes.append(ext4_blocks_accessed)
+
+ def handle_req_complete(self, time, is_read):
+ self.fevent.add_access(is_read, self.time, self.lblk, self.len, self.process_name,\
+ self.access_sizes)
+
+ def handle_merged_req(self, time, offsets, lens, names, is_read):
+ self.fevent.add_merged_access(is_read, time, offsets, lens, names)
+
+ def is_req_complete(self):
+ return len(self.pending_accesses) == 0
+
+ def is_req_started(self):
+ return self.len is not len(self.pending_accesses)
class Trace:
def __init__(self):
self.files_per_device = {} # key: device, value: { key: inode, value; FileEvent }
self.re_open = re.compile(RE_DO_SYS_OPEN)
- self.re_read = re.compile(RE_EXT4_MA_BLOCKS_ENTER)
+ self.re_ext4_access = re.compile(RE_EXT4_MA_BLOCKS_EXIT)
+ self.re_bio_remap = re.compile(RE_BLOCK_BIO_REMAP)
+ self.re_block_issue = re.compile(RE_BLOCK_RQ_ISSUE)
+ # req from ext4 that has not gone down to block level yet, K:block address,
+ # V: list of PendingRead
+ self.pending_accesses = {}
+ self.remap = {}
+ self.process_names = {} # K: PID, V : name
def handle_line(self, line):
match = self.re_open.match(line)
if match:
self.handle_open(match)
return
- match = self.re_read.match(line)
+ match = self.re_ext4_access.match(line)
if match:
- self.handle_read(match)
+ self.handle_ext4_block_exit(match)
+ return
+ match = self.re_bio_remap.match(line)
+ if match:
+ self.handle_bio_remap(match)
+ return
+ match = self.re_block_issue.match(line)
+ if match:
+ self.handle_block_issue(match)
return
def handle_open(self, match):
- time = match.group(1)
- process_name = match.group(2)
- file_name = match.group(3)
- flag = match.group(4)
- inode = match.group(5)
+ pid = int(match.group(1))
+ time = match.group(2)
+ process_name = match.group(3)
+ file_name = match.group(4)
+ flag = match.group(5)
+ inode = int(match.group(6))
dev_name = None
+ self.process_names[pid] = process_name
+ #print "open", pid, process_name, file_name, inode
for p in PARTITION_TO_DEVICE:
if file_name.startswith(p):
dev_name = PARTITION_TO_DEVICE[p]
@@ -123,14 +281,24 @@
else:
fevent.add_open(time, process_name, flag)
- def handle_read(self, match):
+ def handle_ext4_block_exit(self, match):
process_name = match.group(1)
- pid = match.group(2)
- time = match.group(3)
+ pid = int(match.group(2))
+ time = float(match.group(3))
dev = match.group(4)
- inode = match.group(5)
- offset = int(match.group(6))
- size = int(match.group(7))
+ inode = int(match.group(5))
+ lblk = int(match.group(6))
+ pblk = int(match.group(7)) * EXT4_SIZE_TO_BLOCK_SIZE # address in ext4 blocks, ...
+ l = int(match.group(8))
+ mflags = match.group(9)
+ ret = int(match.group(10))
+ if ret <= 0: # no block access
+ return
+ process_name = self.process_names.get(pid, process_name)
+ if process_name == '<...>':
+ process_name = "pid-" + str(pid)
+ if DBG_ISSUE:
+ print "ext4", pblk, l, inode, process_name
files = self.files_per_device.get(dev)
if not files:
if DEVICE_TO_PARTITION.get(dev):
@@ -138,45 +306,123 @@
self.files_per_device[dev] = files
else:
if DBG:
- print "read ignored for device", dev
+ print "access ignored for device", dev
return
fevent = files.get(inode)
if not fevent:
if DBG:
print 'no open for device %s with inode %s' % (dev, inode)
- fevent = FileEvent(time, "unknown", process_name, inode, 0)
+ fevent = FileEvent(time, "unknown", process_name, inode, "-")
files[inode] = fevent
- fevent.add_read(time, offset, size, process_name + "-" + pid)
+ pending_access = PendingAccess(process_name, pid, time, dev, inode, lblk, pblk, l,\
+ fevent)
+ access_list = self.pending_accesses.get(pblk, [])
+ access_list.append(pending_access)
+ self.pending_accesses[pblk] = access_list
+ def handle_bio_remap(self, match):
+ new_addr = int(match.group(1))
+ old_addr = int(match.group(2))
+ self.remap[new_addr] = old_addr
+ if DBG_ISSUE:
+ print "remap", new_addr, old_addr
+
+ def handle_block_issue(self, match):
+ pid = int(match.group(1))
+ time = float(match.group(2))
+ dev_major = match.group(3)
+ dev_minor = match.group(4)
+ access = match.group(5)
+ new_address = int(match.group(6))
+ l = int(match.group(7))
+ name = match.group(8)
+ name = self.process_names.get(pid, name)
+ if name == '<...>':
+ name = "pid-" + str(pid)
+ is_read = not 'W' in access
+ accesses_per_inodes = {} # K:inodes, V: list of two entries, 1st: offsets, 2nd: length
+ addrs_to_remove = []
+ completed_reqs = []
+ address = self.remap.get(new_address, new_address)
+ if DBG_ISSUE:
+ print "issue", address, l, is_read, access
+ for access_addr, access_list in self.pending_accesses.iteritems():
+ if (address >= access_addr) and (address + l) > access_addr:
+ reqs_to_remove = []
+ for pending in access_list:
+ offset, valid_access_size = pending.get_valid_access(address - access_addr, l)
+ if valid_access_size > 0:
+ if pending.is_req_started(): # spread across multiple reads. complete alone
+ pending.queue_block_access(offset, valid_access_size)
+ if pending.is_req_complete():
+ pending.handle_req_complete(time, is_read)
+ reqs_to_remove.append(pending)
+ else: # possible multiple reads completed in this read. complete them together
+ pending.queue_block_access(offset, valid_access_size)
+ if pending.is_req_complete():
+ reads = accesses_per_inodes.get(pending.inode, [[], [], []])
+ reads[0].append(offset + pending.lblk)
+ reads[1].append(valid_access_size)
+ reads[2].append(pending.process_name)
+ accesses_per_inodes[pending.inode] = reads
+ completed_reqs.append(pending)
+ reqs_to_remove.append(pending)
+ for to_remove in reqs_to_remove:
+ access_list.remove(to_remove)
+ if len(access_list) == 0:
+ addrs_to_remove.append(access_addr)
+ for addr in addrs_to_remove:
+ del self.pending_accesses[addr]
+ for pending in completed_reqs: # these will be reported as batch
+ accesses = accesses_per_inodes.get(pending.inode)
+ if not accesses: # merged one already dispatched
+ continue
+ if len(accesses[0]) == 1:
+ pending.handle_req_complete(time, is_read)
+ else: #merged
+ pending.handle_merged_req(time, accesses[0], accesses[1], accesses[2], is_read)
+ del accesses_per_inodes[pending.inode]
def dump_partition(self, partition_name, files):
- print "**Dump partition:", partition_name, "toal number of files:", len(files)
+ name_to_pid_map = {}
+ for pid, name in self.process_names.iteritems():
+ name_to_pid_map[name] = str(pid)
+ print "**Dump partition:", partition_name, "total number of files:", len(files)
total_reads = 0
- total_rereads = 0
- vs = files.values()
- vs.sort(key=lambda f : f.total_reads, reverse = True)
- for f in vs:
- f.dump()
- total_reads += f.total_reads
- total_rereads += f.total_rereads
- print " Total reads for partition", total_reads, "rereads", total_rereads
- return total_reads, total_rereads, len(files)
+ total_writes = 0
+ files_sorted_by_read = files.values()
+ files_sorted_by_read.sort(key=lambda f : f.read.total_access, reverse = True)
+ files_sorted_by_write = files.values()
+ files_sorted_by_write.sort(key=lambda f : f.write.total_access, reverse = True)
+ print " Top 10 readers:"
+ for i in range(min(10, len(files_sorted_by_read))):
+ files_sorted_by_read[i].dump_short()
+ print " Top 10 writers:"
+ for i in range(min(10, len(files_sorted_by_write))):
+ files_sorted_by_write[i].dump_short()
+ for f in files_sorted_by_read:
+ f.dump(name_to_pid_map)
+ total_reads += f.read.total_access
+ total_writes += f.write.total_access
+ print " Total reads:", total_reads, " total writes:", total_writes
+ return total_reads, total_writes, len(files)
def dump(self):
- print "Dump read per each partition"
+ print "*Dump R/W per each partition"
total_reads = 0
- total_rereads = 0
+ total_writes = 0
summaries = []
for d in self.files_per_device:
- reads, rereads, num_files = self.dump_partition(DEVICE_TO_PARTITION[d], \
+ reads, writes, num_files = self.dump_partition(DEVICE_TO_PARTITION[d], \
self.files_per_device[d])
total_reads += reads
- total_rereads += rereads
- summaries.append((DEVICE_TO_PARTITION[d], reads, rereads, num_files))
- print "**Summary**"
- print "Total blocks read", total_reads, "reread", total_rereads
- print "Partition total_reads total_rereads num_files"
+ total_writes += writes
+ summaries.append((DEVICE_TO_PARTITION[d], reads, writes, num_files))
+ print "*Summary*"
+ print "Total blocks read", total_reads
+ print "Total blocks wrote", total_writes
+ print "Partition total_reads total_writes num_files"
for s in summaries:
print s[0], s[1], s[2], s[3]
diff --git a/tools/io_analysis/check_io_trace_all.py b/tools/io_analysis/check_io_trace_all.py
index 586a677..8ea466d 100644
--- a/tools/io_analysis/check_io_trace_all.py
+++ b/tools/io_analysis/check_io_trace_all.py
@@ -22,7 +22,12 @@
import string
import sys
-RE_BLOCK = r'.+-([0-9]+).*\s+([0-9]+\.[0-9]+):\s+block_bio_queue:\s+([0-9]+)\,([0-9]+)\s+([RW]\S*)\s+([0-9]+)\s+\+\s+([0-9]+)\s+\[([^\]]+)'
+# ex) <...>-52 [001] ...1 1.362574: block_bio_queue: 8,16 R 0 + 8 [kworker/u8:1]
+RE_BLOCK = r'.+-([0-9]+).*\s+([0-9]+\.[0-9]+):\s+block_bio_queue:\s+([0-9]+)\,([0-9]+)\s(\S+)\s+([0-9]+)\s+\+\s+([0-9]+)\s+\[([^\]]+)'
+# ex) <...>-453 [001] d..4 3.181854: sched_blocked_reason: pid=471 iowait=1 caller=__wait_on_buffer+0x24/0x2c
+RE_SCHED_BLOCKED_READSON = r'.+-([0-9]+)\s+\[([0-9]+)\]\s.*\s+([0-9]+\.[0-9]+):\s+sched_blocked_reason:\spid=([0-9]+)\siowait=([01])\scaller=(\S+)'
+# ex) <idle>-0 [000] d..3 3.181864: sched_switch: prev_comm=swapper/0 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=ueventd next_pid=471 next_prio=120
+RE_SCHED_SWITCH = r'.+-([0-9]+)\s+\[([0-9]+)\]\s.*\s+([0-9]+\.[0-9]+):\s+sched_switch:\sprev_comm=(.+)\sprev_pid=([0-9]+)\sprev_prio=([0-9]+)\sprev_state=(\S+).*next_comm=(.+)\snext_pid=([0-9]+)\snext_prio=([0-9]+)'
# dev_num = major * MULTIPLIER + minor
DEV_MAJOR_MULTIPLIER = 1000
@@ -56,6 +61,7 @@
self.total_dm_reads = 0
self.total_dm_writes = 0
+
def add_read_event(self, major, minor, event):
devNum = major * DEV_MAJOR_MULTIPLIER + minor;
events = self.reads.get(devNum)
@@ -104,7 +110,7 @@
dev = sorted_w.popitem(last=False)
print " ", dev[0],dev[1]
-class Trace:
+class IoTrace:
def __init__(self):
self.ios = {} #K: process name, v:ProcessData
@@ -114,10 +120,22 @@
self.total_writes_per_device = {}
self.total_dm_reads = {} #K: devnum, V: blocks
self.total_dm_writes = {}
+ self.re_block = re.compile(RE_BLOCK)
- def parse_bio_queue(self, l, match):
+ def parse(self, l):
+ match = self.re_block.match(l)
+ if not match:
+ return False
+ try:
+ self.do_parse_bio_queue(l, match)
+ except ValueError:
+ print "cannot parse:", l
+ raise
+ return True
+
+ def do_parse_bio_queue(self, l, match):
pid = match.group(1)
- start_time = int(float(match.group(2))*1000000) #us
+ start_time = float(match.group(2))*1000 #ms
major = int(match.group(3))
minor = int(match.group(4))
devNum = major * DEV_MAJOR_MULTIPLIER + minor;
@@ -132,25 +150,25 @@
self.ios[process] = io
if major == DM_MAJOR:
devNum = major * DEV_MAJOR_MULTIPLIER + minor;
- if operation[0] == 'R':
+ if 'R' in operation[0]:
if devNum not in self.total_dm_reads:
self.total_dm_reads[devNum] = 0
self.total_dm_reads[devNum] += size
io.add_dm_read(size)
- elif operation[0] == 'W':
+ elif 'W' in operation[0]:
if devNum not in self.total_dm_writes:
self.total_dm_writes[devNum] = 0
self.total_dm_writes[devNum] += size
io.add_dm_write(size)
return
- if operation[0] == 'R':
+ if 'R' in operation[0]:
io.add_read_event(major, minor, event)
self.total_reads += size
per_device = self.total_reads_per_device.get(devNum)
if not per_device:
self.total_reads_per_device[devNum] = 0
self.total_reads_per_device[devNum] += size
- elif operation[0] == 'W':
+ elif 'W' in operation[0]:
io.add_write_event(major, minor, event)
self.total_writes += size
per_device = self.total_writes_per_device.get(devNum)
@@ -158,13 +176,6 @@
self.total_writes_per_device[devNum] = 0
self.total_writes_per_device[devNum] += size
- def parse_block_trace(self, l, match):
- try:
- self.parse_bio_queue(l, match)
- except ValueError:
- print "cannot parse:", l
- raise
-
def dump(self):
print "total read blocks,", self.total_reads
print "total write blocks,", self.total_writes
@@ -206,20 +217,170 @@
dev = sorted_by_total_w.popitem(last=False)
print dev[0],dev[1]
+class SchedProcess:
+ def __init__(self, pid):
+ self.pid = pid
+ self.name = "unknown"
+ self.total_execution_time = 0.0
+ self.total_io_wait_time = 0.0
+ self.total_other_wait_time = 0.0
+ self.waiting_calls = {} # k: waiting_call, v : waiting counter
+ self.io_waiting_call_times = {} # k: waiting_call, v: total wait time
+ self.in_iowait = False
+ self.last_waiting_call = None
+ self.last_switch_out_time = 0.0
+ self.last_switch_in_time = 0.0
+ self.last_core = -1
+ self.execution_time_per_core = {} # k: core, v : time
+ self.io_latency_histograms = {} # k : delay in ms, v : count
+
+ def handle_reason(self, current_time, iowait, waiting_call):
+ #if self.pid == 1232:
+ # print current_time, iowait, waiting_call
+ if iowait == 1:
+ self.in_iowait = True
+ self.last_waiting_call = waiting_call
+ call_counter = self.waiting_calls.get(waiting_call, 0)
+ call_counter += 1
+ self.waiting_calls[waiting_call] = call_counter
+
+ def handle_switch_out(self, current_time, out_state, priority, name, core):
+ #if self.pid == 1232:
+ # print "out", current_time, out_state
+ if self.name != name:
+ self.name = name
+ self.last_switch_out_time = current_time
+ if self.last_switch_in_time == 0.0: # switch in not recorded. ignore this one
+ return
+ execution_time = current_time - self.last_switch_in_time
+ self.total_execution_time += execution_time
+ core_execution_time = self.execution_time_per_core.get(core, 0.0)
+ core_execution_time += execution_time
+ self.execution_time_per_core[core] = core_execution_time
+
+ def handle_switch_in(self, current_time, priority, name, core):
+ #if self.pid == 1232:
+ # print "in", current_time, self.in_iowait
+ if self.name != name:
+ self.name = name
+ self.last_switch_in_time = current_time
+ if self.last_switch_out_time == 0.0: # in without out, probably 1st
+ self.in_iowait = False
+ return
+ wait_time = current_time - self.last_switch_out_time
+ if self.in_iowait:
+ self.total_io_wait_time += wait_time
+ total_waiting_call_time = self.io_waiting_call_times.get(self.last_waiting_call, 0.0)
+ total_waiting_call_time += wait_time
+ self.io_waiting_call_times[self.last_waiting_call] = total_waiting_call_time
+ wait_time_ms = int(wait_time*10) / 10.0 # resolution up to 0.1 ms
+ histogram_count = self.io_latency_histograms.get(wait_time_ms, 0)
+ histogram_count += 1
+ self.io_latency_histograms[wait_time_ms] = histogram_count
+ else:
+ self.total_other_wait_time += wait_time
+ self.in_iowait = False
+
+
+ def dump(self):
+ print "PID:", self.pid, " name:", self.name
+ print " total execution time:", self.total_execution_time,\
+ " io wait:", self.total_io_wait_time, " other wait:", self.total_other_wait_time
+ sorted_data = collections.OrderedDict(sorted(self.execution_time_per_core.items(), \
+ key = lambda item: item[0], reverse = False))
+ print " Core execution:", sorted_data
+ sorted_data = collections.OrderedDict(sorted(self.waiting_calls.items(), \
+ key = lambda item: item[1], reverse = True))
+ print " Wait calls:", sorted_data
+ sorted_data = collections.OrderedDict(sorted(self.io_waiting_call_times.items(), \
+ key = lambda item: item[1], reverse = True))
+ print " IO Wait time per wait calls:", sorted_data
+ sorted_data = collections.OrderedDict(sorted(self.io_latency_histograms.items(), \
+ key = lambda item: item[0], reverse = False))
+ print " Wait time histogram:", sorted_data
+
+class SchedTrace:
+ def __init__(self):
+ self.re_switch = re.compile(RE_SCHED_SWITCH)
+ self.re_reason = re.compile(RE_SCHED_BLOCKED_READSON)
+ self.processes = {} # key: pid, v : SchedProcess
+
+ def parse(self, l):
+ checked_reason = False
+ match = self.re_switch.match(l)
+ if not match:
+ match = self.re_reason.match(l)
+ checked_reason = True
+ if not match:
+ return False
+ try:
+ if checked_reason:
+ self.do_handle_reason(l, match)
+ else:
+ self.do_handle_switch(l, match)
+ except ValueError:
+ print "cannot parse:", l
+ raise
+ return True
+
+ def do_handle_switch(self, l, match):
+ current_pid = int(match.group(1))
+ cpu_core = int(match.group(2))
+ current_time = float(match.group(3))*1000 #ms
+ out_name = match.group(4)
+ out_pid = int(match.group(5))
+ out_prio = int(match.group(6))
+ out_state = match.group(7)
+ in_name = match.group(8)
+ in_pid = int(match.group(9))
+ in_prio = int(match.group(10))
+ out_process = self.processes.get(out_pid)
+ if not out_process:
+ out_process = SchedProcess(out_pid)
+ self.processes[out_pid] = out_process
+ in_process = self.processes.get(in_pid)
+ if not in_process:
+ in_process = SchedProcess(in_pid)
+ self.processes[in_pid] = in_process
+ out_process.handle_switch_out(current_time, out_state, out_prio, out_name, cpu_core)
+ in_process.handle_switch_in(current_time, in_prio, in_name, cpu_core)
+
+ def do_handle_reason(self, l, match):
+ current_pid = int(match.group(1))
+ cpu_core = int(match.group(2))
+ current_time = float(match.group(3))*1000 #ms
+ pid = int(match.group(4))
+ iowait = int(match.group(5))
+ waiting_call = match.group(6)
+ process = self.processes.get(pid)
+ if not process:
+ process = SchedProcess(pid)
+ self.processes[pid] = process
+ process.handle_reason(current_time, iowait, waiting_call)
+
+ def dump(self):
+ sorted_by_total_execution = collections.OrderedDict(sorted(self.processes.items(), \
+ key = lambda item: item[1].total_io_wait_time, reverse = True))
+ for k, v in sorted_by_total_execution.iteritems():
+ if v.total_execution_time > 10.0 or v.total_io_wait_time != 0.0:
+ v.dump()
+
def main(argv):
if (len(argv) < 2):
print "check_io_trace_all.py filename"
return
filename = argv[1]
- trace = Trace()
- prog = re.compile(RE_BLOCK)
+ io_trace = IoTrace()
+ sched_trace = SchedTrace()
with open(filename) as f:
for l in f:
- result = prog.match(l)
- if result:
- trace.parse_block_trace(l, result)
- trace.dump()
+ if io_trace.parse(l):
+ continue
+ sched_trace.parse(l)
+ io_trace.dump()
+ print "\n\n\n"
+ sched_trace.dump()
if __name__ == '__main__':
main(sys.argv)
diff --git a/tools/update-obd2-sensors.py b/tools/update-obd2-sensors.py
index 7f22f52..e161258 100755
--- a/tools/update-obd2-sensors.py
+++ b/tools/update-obd2-sensors.py
@@ -109,22 +109,51 @@
str(sensorId) + ";"
def prefix(self, theSensors):
- s = " public static final class Obd2%sSensorIndex {\n" % \
- theSensors.descriptor
- s += " private Obd2%sSensorIndex() {}\n" % \
- theSensors.descriptor
+ s = \
+"/*\n" + \
+" * Copyright (C) 2017 The Android Open Source Project\n" + \
+" *\n" + \
+" * Licensed under the Apache License, Version 2.0 (the \"License\");\n" + \
+" * you may not use this file except in compliance with the License.\n" + \
+" * You may obtain a copy of the License at\n" + \
+" *\n" + \
+" * http://www.apache.org/licenses/LICENSE-2.0\n" + \
+" *\n" + \
+" * Unless required by applicable law or agreed to in writing, software\n" + \
+" * distributed under the License is distributed on an \"AS IS\" BASIS,\n" + \
+" * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + \
+" * See the License for the specific language governing permissions and\n" + \
+" * limitations under the License.\n" + \
+"*/\n" + \
+"\n" + \
+"package android.car.diagnostic;\n" + \
+"\n" + \
+"import android.annotation.IntDef;\n" + \
+"import android.annotation.SystemApi;\n" + \
+"import java.lang.annotation.Retention;\n" + \
+"import java.lang.annotation.RetentionPolicy;\n" + \
+"\n" + \
+"/**\n" + \
+" * This class is a container for the indices of diagnostic sensors. The values are extracted by\n" + \
+" * running packages/services/Car/tools/update-obd2-sensors.py against types.hal.\n" + \
+" *\n" + \
+" * DO NOT EDIT MANUALLY\n" + \
+" *\n" + \
+" * @hide\n" + \
+" */\n" + \
+"@SystemApi\n" + \
+"public final class %sSensorIndex {\n" % theSensors.descriptor + \
+" private %sSensorIndex() {}\n" % theSensors.descriptor
+
return s
- def suffix(self, theSensors):
- return " }"
-
def indent(self):
- return 8
+ return 4
class PythonSensorPolicy(SensorPolicy):
"""The sensor policy that emits Python sensor descriptions."""
def sensor(self, theSensor, theSensors):
- return "OBD2_SENSOR_%s_%s = %s" % (
+ return "DIAGNOSTIC_SENSOR_%s_%s = %s" % (
theSensors.descriptor.upper(),
theSensor.name.upper(),
self.adjustSensorId(theSensors.descriptor.upper(), str(theSensor.id))
@@ -132,23 +161,22 @@
def adjustSensorId(self, descriptor, sensorId):
if sensorId.isdigit(): return sensorId
- return "OBD2_SENSOR_%s_%s" % (descriptor, sensorId.upper())
+ return "DIAGNOSTIC_SENSOR_%s_%s" % (descriptor, sensorId.upper())
class IntDefSensorPolicy(SensorPolicy):
"""The sensor policy that emits @IntDef sensor descriptions."""
def sensor(self, theSensor, theSensors):
sensorName = theSensor.name.replace("_INDEX", "")
- return "Obd2%sSensorIndex.%s," % (theSensors.descriptor,sensorName)
+ return "%sSensorIndex.%s," % (theSensors.descriptor,sensorName)
def prefix(self, theSensors):
- return " @Retention(RetentionPolicy.SOURCE)\n @IntDef({"
+ return " /** @hide */\n @Retention(RetentionPolicy.SOURCE)\n @IntDef({"
def indent(self):
return 8
def suffix(self, theSensors):
- return " })\n public @interface %sSensorIndex {}" % \
- theSensors.descriptor
+ return " })\n public @interface SensorIndex {}"
class SensorMeta(type):
"""Metaclass for sensor classes."""
@@ -182,7 +210,15 @@
def applyPolicy(policy, destfile):
"""Given a sensor policy, apply it to all known sensor types"""
+ applyIntPolicy(policy, destfile)
+ applyFloatPolicy(policy, destfile)
+
+def applyIntPolicy(policy, destfile):
+ "Given a sensor policy, apply it to integer sensors"
print(policy.sensors(intSensors), file=destfile)
+
+def applyFloatPolicy(policy, destfile):
+ "Given a sensor policy, apply it to float sensors"
print(policy.sensors(floatSensors), file=destfile)
def java(destfile):
@@ -196,41 +232,18 @@
def generateJava(filepath):
"""Generate Java code for all sensors."""
- destfile = open(filepath, "w")
- print("/*", file=destfile)
- print(" * Copyright (C) 2017 The Android Open Source Project", file=destfile)
- print(" *", file=destfile)
- print(" * Licensed under the Apache License, Version 2.0 (the \"License\");", file=destfile)
- print(" * you may not use this file except in compliance with the License.", file=destfile)
- print(" * You may obtain a copy of the License at", file=destfile)
- print(" *", file=destfile)
- print(" * http://www.apache.org/licenses/LICENSE-2.0", file=destfile)
- print(" *", file=destfile)
- print(" * Unless required by applicable law or agreed to in writing, software", file=destfile)
- print(" * distributed under the License is distributed on an \"AS IS\" BASIS,", file=destfile)
- print(" * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", file=destfile)
- print(" * See the License for the specific language governing permissions and", file=destfile)
- print(" * limitations under the License.", file=destfile)
- print("*/", file=destfile)
- print("", file=destfile)
- print("package android.car.hardware;", file=destfile)
- print("", file=destfile)
- print("import android.annotation.IntDef;", file=destfile)
- print("import java.lang.annotation.Retention;", file=destfile)
- print("import java.lang.annotation.RetentionPolicy;", file=destfile)
- print("", file=destfile)
- print("/**", file=destfile)
- print(" * This class is a container for the indices of integer and float diagnostic sensors.", file=destfile)
- print(" * These values are extracted from types.hal by packages/services/Car/tools/update-obd2-sensors.py", file=destfile)
- print(" *", file=destfile)
- print(" * DO NOT EDIT MANUALLY", file=destfile)
- print(" *", file=destfile)
- print(" * @hide", file=destfile)
- print(" */", file=destfile)
- print("public final class CarDiagnosticSensorIndices {", file=destfile)
- java(destfile)
- intdef(destfile)
- print("}", file=destfile)
+ intfile = open(os.path.join(filepath, "IntegerSensorIndex.java"), "w")
+ floatfile = open(os.path.join(filepath, "FloatSensorIndex.java"), "w")
+ javaPolicy = JavaSensorPolicy()
+ intdefPolicy = IntDefSensorPolicy()
+ applyIntPolicy(javaPolicy, intfile)
+ applyIntPolicy(intdefPolicy, intfile)
+ applyFloatPolicy(javaPolicy, floatfile)
+ applyFloatPolicy(intdefPolicy, floatfile)
+ print("}", file=intfile)
+ print("}", file=floatfile)
+ intfile.close()
+ floatfile.close()
def generatePython(filepath):
"""Generate Python code for all sensors."""
@@ -258,8 +271,8 @@
def load(filepath):
"""Load sensor data from Vehicle HAL."""
ast = hidl_parser.parser.parse(filepath)
- integerSensors = ast['enums']['Obd2IntegerSensorIndex']
- floatSensors = ast['enums']['Obd2FloatSensorIndex']
+ integerSensors = ast['enums']['DiagnosticIntegerSensorIndex']
+ floatSensors = ast['enums']['DiagnosticFloatSensorIndex']
for case in integerSensors.cases:
intSensor(name=case.name, id=case.value)
for case in floatSensors.cases:
@@ -268,9 +281,9 @@
import os
if len(sys.argv) != 4:
- print('syntax: update-obd2-sensors.py <path/to/types.hal> <path/to/CarDiagnosticSensorIndices.java> <path/to/diagnostic_sensors.py>')
+ print('syntax: update-obd2-sensors.py <path/to/types.hal> <path/to/android.car.diagnostic> <path/to/diagnostic_sensors.py>')
print('This script will parse types.hal, and use the resulting', end='')
- print('parse tree to generate CarDiagnosticSensorIndices.java.')
+ print('parse tree to generate Java and Python lists of sensor identifiers.')
sys.exit(1)
load(sys.argv[1])
generateJava(sys.argv[2])
diff --git a/vehicle-hal-support-lib/Android.mk b/vehicle-hal-support-lib/Android.mk
index 9ff39c6..cd24b59 100644
--- a/vehicle-hal-support-lib/Android.mk
+++ b/vehicle-hal-support-lib/Android.mk
@@ -26,7 +26,6 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
android.hidl.base-V1.0-java \
android.hardware.automotive.vehicle-V2.0-java \
- android.hardware.automotive.vehicle-V2.1-java \
junit \
legacy-android-test
diff --git a/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticEventBuilder.java b/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticEventBuilder.java
index 1e0c237..d000e7a 100644
--- a/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticEventBuilder.java
+++ b/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticEventBuilder.java
@@ -18,8 +18,8 @@
import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
-import android.hardware.automotive.vehicle.V2_1.Obd2FloatSensorIndex;
-import android.hardware.automotive.vehicle.V2_1.Obd2IntegerSensorIndex;
+import android.hardware.automotive.vehicle.V2_0.DiagnosticFloatSensorIndex;
+import android.hardware.automotive.vehicle.V2_0.DiagnosticIntegerSensorIndex;
import android.util.SparseArray;
import java.util.BitSet;
import java.util.Iterator;
@@ -109,9 +109,9 @@
public DiagnosticEventBuilder(
int propertyId, int numVendorIntSensors, int numVendorFloatSensors) {
mPropertyId = propertyId;
- mNumIntSensors = Obd2IntegerSensorIndex.LAST_SYSTEM_INDEX + 1 + numVendorIntSensors;
+ mNumIntSensors = DiagnosticIntegerSensorIndex.LAST_SYSTEM_INDEX + 1 + numVendorIntSensors;
final int numFloatSensors =
- Obd2FloatSensorIndex.LAST_SYSTEM_INDEX + 1 + numVendorFloatSensors;
+ DiagnosticFloatSensorIndex.LAST_SYSTEM_INDEX + 1 + numVendorFloatSensors;
mBitmask = new BitSet(mNumIntSensors + numFloatSensors);
mIntValues = new DefaultedArray<>(mNumIntSensors, 0);
mFloatValues = new DefaultedArray<>(numFloatSensors, 0.0f);
diff --git a/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticJsonReader.java b/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticJsonReader.java
index 5dd6a66..b3d0c02 100644
--- a/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticJsonReader.java
+++ b/vehicle-hal-support-lib/src/com/android/car/vehiclehal/DiagnosticJsonReader.java
@@ -16,8 +16,8 @@
package com.android.car.vehiclehal;
-import static android.hardware.automotive.vehicle.V2_1.VehicleProperty.OBD2_FREEZE_FRAME;
-import static android.hardware.automotive.vehicle.V2_1.VehicleProperty.OBD2_LIVE_FRAME;
+import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.OBD2_FREEZE_FRAME;
+import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.OBD2_LIVE_FRAME;
import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
diff --git a/vehicle-hal-support-lib/src/com/android/car/vehiclehal/test/MockedVehicleHal.java b/vehicle-hal-support-lib/src/com/android/car/vehiclehal/test/MockedVehicleHal.java
index 1a5b8c6..0da3565 100644
--- a/vehicle-hal-support-lib/src/com/android/car/vehiclehal/test/MockedVehicleHal.java
+++ b/vehicle-hal-support-lib/src/com/android/car/vehiclehal/test/MockedVehicleHal.java
@@ -21,8 +21,6 @@
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.fail;
-import com.google.android.collect.Lists;
-
import android.hardware.automotive.vehicle.V2_0.IVehicle;
import android.hardware.automotive.vehicle.V2_0.IVehicleCallback;
import android.hardware.automotive.vehicle.V2_0.StatusCode;
@@ -31,6 +29,9 @@
import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
import android.os.RemoteException;
+import android.os.SystemClock;
+
+import com.google.android.collect.Lists;
import java.util.ArrayList;
import java.util.HashMap;
@@ -74,6 +75,23 @@
addProperty(config, new StaticPropertyHandler(value));
}
+ public boolean waitForSubscriber(int propId, long timeoutMillis) {
+ long startTime = SystemClock.elapsedRealtime();
+ try {
+ synchronized (this) {
+ while (mSubscribers.get(propId) == null) {
+ long waitMillis = startTime - SystemClock.elapsedRealtime() + timeoutMillis;
+ if (waitMillis < 0) break;
+ wait(waitMillis);
+ }
+
+ return mSubscribers.get(propId) != null;
+ }
+ } catch (InterruptedException e) {
+ return false;
+ }
+ }
+
public synchronized void injectEvent(VehiclePropValue value) {
List<IVehicleCallback> callbacks = mSubscribers.get(value.prop);
assertNotNull("Injecting event failed for property: " + value.prop
@@ -156,6 +174,7 @@
if (subscribers == null) {
subscribers = new ArrayList<>();
mSubscribers.put(opt.propId, subscribers);
+ notifyAll();
}
subscribers.add(callback);
}