Use -Werror in packages/services/Car/tools am: 7f50f1e4bd am: 20fdefebcf
am: 3022022eff
Change-Id: I945c8e641d79a885523ea8d196bde06f54443586
diff --git a/Android.mk b/Android.mk
index b33eecc..04f8004 100644
--- a/Android.mk
+++ b/Android.mk
@@ -17,7 +17,6 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-CAR_CURRENT_SDK_VERSION := 23
CAR_API_CHECK := $(LOCAL_PATH)/apicheck.mk
api_check_current_msg_file := $(LOCAL_PATH)/apicheck_msg_current.txt
api_check_last_msg_file := $(LOCAL_PATH)/apicheck_msg_last.txt
@@ -28,7 +27,6 @@
include $(call all-makefiles-under,$(LOCAL_PATH))
# Clear out variables
-CAR_CURRENT_SDK_VERSION :=
CAR_API_CHECK :=
api_check_current_msg_file :=
api_check_last_msg_file :=
diff --git a/car-lib/api/system-current.txt b/car-lib/api/system-current.txt
index 6f32dd7..9a3fe1d 100644
--- a/car-lib/api/system-current.txt
+++ b/car-lib/api/system-current.txt
@@ -32,11 +32,13 @@
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_STORAGE_MONITORING = "android.car.permission.STORAGE_MONITORING";
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 STORAGE_MONITORING_SERVICE = "storage_monitoring";
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 = 3; // 0x3
@@ -485,6 +487,7 @@
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 boolean isSelectiveClearFreezeFramesSupported() 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);
@@ -998,6 +1001,93 @@
}
+package android.car.storagemonitoring {
+
+ public final class CarStorageMonitoringManager {
+ method public java.util.List<android.car.storagemonitoring.UidIoStats> getAggregateIoStats() throws android.car.CarNotConnectedException;
+ method public java.util.List<android.car.storagemonitoring.UidIoStats> getBootIoStats() throws android.car.CarNotConnectedException;
+ method public int getPreEolIndicatorStatus() throws android.car.CarNotConnectedException;
+ method public android.car.storagemonitoring.WearEstimate getWearEstimate() throws android.car.CarNotConnectedException;
+ method public java.util.List<android.car.storagemonitoring.WearEstimateChange> getWearEstimateHistory() throws android.car.CarNotConnectedException;
+ field public static final int PRE_EOL_INFO_NORMAL = 1; // 0x1
+ field public static final int PRE_EOL_INFO_UNKNOWN = 0; // 0x0
+ field public static final int PRE_EOL_INFO_URGENT = 3; // 0x3
+ field public static final int PRE_EOL_INFO_WARNING = 2; // 0x2
+ }
+
+ public final class UidIoStats implements android.os.Parcelable {
+ ctor public UidIoStats(int, long, android.car.storagemonitoring.UidIoStats.PerStateMetrics, android.car.storagemonitoring.UidIoStats.PerStateMetrics);
+ ctor public UidIoStats(android.os.Parcel);
+ ctor public UidIoStats(android.car.storagemonitoring.UidIoStatsRecord, long);
+ ctor public UidIoStats(org.json.JSONObject) throws org.json.JSONException;
+ method public int describeContents();
+ 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.storagemonitoring.UidIoStats> CREATOR;
+ field public final android.car.storagemonitoring.UidIoStats.PerStateMetrics background;
+ field public final android.car.storagemonitoring.UidIoStats.PerStateMetrics foreground;
+ field public final long runtimeMillis;
+ field public final int uid;
+ }
+
+ public static final class UidIoStats.PerStateMetrics implements android.os.Parcelable {
+ ctor public UidIoStats.PerStateMetrics(long, long, long, long, long);
+ ctor public UidIoStats.PerStateMetrics(android.os.Parcel);
+ ctor public UidIoStats.PerStateMetrics(org.json.JSONObject) throws org.json.JSONException;
+ method public int describeContents();
+ 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.storagemonitoring.UidIoStats.PerStateMetrics> CREATOR;
+ field public final long bytesRead;
+ field public final long bytesReadFromStorage;
+ field public final long bytesWritten;
+ field public final long bytesWrittenToStorage;
+ field public final long fsyncCalls;
+ }
+
+ public final class UidIoStatsRecord {
+ ctor public UidIoStatsRecord(int, long, long, long, long, long, long, long, long, long, long);
+ field public final long background_fsync;
+ field public final long background_rchar;
+ field public final long background_read_bytes;
+ field public final long background_wchar;
+ field public final long background_write_bytes;
+ field public final long foreground_fsync;
+ field public final long foreground_rchar;
+ field public final long foreground_read_bytes;
+ field public final long foreground_wchar;
+ field public final long foreground_write_bytes;
+ field public final int uid;
+ }
+
+ public class WearEstimate implements android.os.Parcelable {
+ ctor public WearEstimate(int, int);
+ ctor public WearEstimate(android.os.Parcel);
+ ctor public WearEstimate(android.util.JsonReader) throws java.io.IOException;
+ method public int describeContents();
+ 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.storagemonitoring.WearEstimate> CREATOR;
+ field public static final int UNKNOWN = -1; // 0xffffffff
+ field public final int typeA;
+ field public final int typeB;
+ }
+
+ public class WearEstimateChange implements android.os.Parcelable {
+ ctor public WearEstimateChange(android.car.storagemonitoring.WearEstimate, android.car.storagemonitoring.WearEstimate, long, java.time.Instant, boolean);
+ ctor public WearEstimateChange(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.storagemonitoring.WearEstimateChange> CREATOR;
+ field public final java.time.Instant dateAtChange;
+ field public final boolean isAcceptableDegradation;
+ field public final android.car.storagemonitoring.WearEstimate newEstimate;
+ field public final android.car.storagemonitoring.WearEstimate oldEstimate;
+ field public final long uptimeAtChange;
+ }
+
+}
+
package android.car.test {
public class CarTestManagerBinderWrapper {
@@ -1008,3 +1098,112 @@
}
+package android.car.vms {
+
+ public final class VmsAssociatedLayer implements android.os.Parcelable {
+ ctor public VmsAssociatedLayer(android.car.vms.VmsLayer, java.util.Set<java.lang.Integer>);
+ method public int describeContents();
+ method public java.util.Set<java.lang.Integer> getPublisherIds();
+ method public android.car.vms.VmsLayer getVmsLayer();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.car.vms.VmsAssociatedLayer> CREATOR;
+ }
+
+ public final class VmsLayer implements android.os.Parcelable {
+ ctor public VmsLayer(int, int, int);
+ method public int describeContents();
+ method public int getSubtype();
+ method public int getType();
+ method public int getVersion();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.car.vms.VmsLayer> CREATOR;
+ }
+
+ public final class VmsLayerDependency implements android.os.Parcelable {
+ ctor public VmsLayerDependency(android.car.vms.VmsLayer, java.util.Set<android.car.vms.VmsLayer>);
+ ctor public VmsLayerDependency(android.car.vms.VmsLayer);
+ method public int describeContents();
+ method public java.util.Set<android.car.vms.VmsLayer> getDependencies();
+ method public android.car.vms.VmsLayer getLayer();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.car.vms.VmsLayerDependency> CREATOR;
+ }
+
+ public final class VmsLayersOffering implements android.os.Parcelable {
+ ctor public VmsLayersOffering(java.util.Set<android.car.vms.VmsLayerDependency>, int);
+ method public int describeContents();
+ method public java.util.Set<android.car.vms.VmsLayerDependency> getDependencies();
+ method public int getPublisherId();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.car.vms.VmsLayersOffering> CREATOR;
+ }
+
+ public final class VmsOperationRecorder {
+ ctor public VmsOperationRecorder(android.car.vms.VmsOperationRecorder.Writer);
+ method public void addHalSubscription(int, android.car.vms.VmsLayer);
+ method public void addPromiscuousSubscription(int);
+ method public void addSubscription(int, android.car.vms.VmsLayer);
+ method public static android.car.vms.VmsOperationRecorder get();
+ method public void getPublisherId(int);
+ method public void removeHalSubscription(int, android.car.vms.VmsLayer);
+ method public void removePromiscuousSubscription(int);
+ method public void removeSubscription(int, android.car.vms.VmsLayer);
+ method public void setHalPublisherLayersOffering(android.car.vms.VmsLayersOffering);
+ method public void setLayersOffering(android.car.vms.VmsLayersOffering);
+ method public void setPublisherLayersOffering(android.car.vms.VmsLayersOffering);
+ method public void startMonitoring();
+ method public void stopMonitoring();
+ method public void subscribe(android.car.vms.VmsLayer);
+ method public void subscribe(android.car.vms.VmsLayer, int);
+ method public void unsubscribe(android.car.vms.VmsLayer);
+ method public void unsubscribe(android.car.vms.VmsLayer, int);
+ }
+
+ public static class VmsOperationRecorder.Writer {
+ ctor public VmsOperationRecorder.Writer();
+ method public boolean isEnabled();
+ method public void write(java.lang.String);
+ }
+
+ public abstract class VmsPublisherClientService extends android.app.Service {
+ ctor public VmsPublisherClientService();
+ method public final int getPublisherId(byte[]);
+ method public final android.car.vms.VmsSubscriptionState getSubscriptions();
+ method public final android.os.IBinder onBind(android.content.Intent);
+ method public final boolean onUnbind(android.content.Intent);
+ method protected abstract void onVmsPublisherServiceReady();
+ method public abstract void onVmsSubscriptionChange(android.car.vms.VmsSubscriptionState);
+ method public final void publish(android.car.vms.VmsLayer, int, byte[]);
+ method public final void setLayersOffering(android.car.vms.VmsLayersOffering);
+ }
+
+ public final class VmsSubscriberManager {
+ ctor public VmsSubscriberManager(android.os.IBinder, android.os.Handler);
+ method public byte[] getPublisherInfo(int) throws android.car.CarNotConnectedException, java.lang.IllegalStateException;
+ method public void registerClientCallback(android.car.vms.VmsSubscriberManager.VmsSubscriberClientCallback) throws android.car.CarNotConnectedException;
+ method public void startMonitoring() throws android.car.CarNotConnectedException;
+ method public void stopMonitoring();
+ method public void subscribe(android.car.vms.VmsLayer) throws android.car.CarNotConnectedException;
+ method public void subscribe(android.car.vms.VmsLayer, int) throws android.car.CarNotConnectedException;
+ method public void unregisterClientCallback() throws android.car.CarNotConnectedException;
+ method public void unsubscribe(android.car.vms.VmsLayer);
+ method public void unsubscribe(android.car.vms.VmsLayer, int);
+ }
+
+ public static abstract interface VmsSubscriberManager.VmsSubscriberClientCallback {
+ method public abstract void onLayersAvailabilityChanged(java.util.List<android.car.vms.VmsLayer>);
+ method public abstract void onVmsMessageReceived(android.car.vms.VmsLayer, byte[]);
+ }
+
+ public final class VmsSubscriptionState implements android.os.Parcelable {
+ ctor public VmsSubscriptionState(int, java.util.Set<android.car.vms.VmsLayer>, java.util.Set<android.car.vms.VmsAssociatedLayer>);
+ method public int describeContents();
+ method public java.util.Set<android.car.vms.VmsAssociatedLayer> getAssociatedLayers();
+ method public java.util.Set<android.car.vms.VmsLayer> getLayers();
+ method public int getSequenceNumber();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.car.vms.VmsSubscriptionState> CREATOR;
+ }
+
+}
+
diff --git a/car-lib/src/android/car/Car.java b/car-lib/src/android/car/Car.java
index 9a9e531..6101797 100644
--- a/car-lib/src/android/car/Car.java
+++ b/car-lib/src/android/car/Car.java
@@ -31,7 +31,9 @@
import android.car.media.CarAudioManager;
import android.car.navigation.CarNavigationStatusManager;
import android.car.CarBluetoothManager;
+import android.car.storagemonitoring.CarStorageMonitoringManager;
import android.car.test.CarTestManagerBinderWrapper;
+import android.car.vms.VmsSubscriberManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -134,6 +136,18 @@
public static final String BLUETOOTH_SERVICE = "car_bluetooth";
/**
+ * @FutureFeature Cannot drop due to usage in non-flag protected place.
+ * @hide
+ */
+ public static final String VMS_SUBSCRIBER_SERVICE = "vehicle_map_subscriber_service";
+
+ /**
+ * @hide
+ */
+ @SystemApi
+ public static final String STORAGE_MONITORING_SERVICE = "storage_monitoring";
+
+ /**
* Service for testing. This is system app only feature.
* Service name for {@link CarTestManager}, to be used in {@link #getCarManager(String)}.
* @hide
@@ -254,6 +268,22 @@
"android.car.permission.CAR_TEST_SERVICE";
/**
+ * Permissions necessary to access VMS publisher APIs.
+ *
+ * @hide
+ */
+ @FutureFeature
+ public static final String PERMISSION_VMS_PUBLISHER = "android.car.permission.VMS_PUBLISHER";
+
+ /**
+ * Permissions necessary to access VMS subscriber APIs.
+ *
+ * @hide
+ */
+ @FutureFeature
+ public static final String PERMISSION_VMS_SUBSCRIBER = "android.car.permission.VMS_SUBSCRIBER";
+
+ /**
* Permissions necessary to read diagnostic information, including vendor-specific bits.
*
* @hide
@@ -270,6 +300,14 @@
@SystemApi
public static final String PERMISSION_CAR_DIAGNOSTIC_CLEAR = "android.car.permission.DIAGNOSTIC_CLEAR";
+ /**
+ * Permissions necessary to clear diagnostic information.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String PERMISSION_STORAGE_MONITORING = "android.car.permission.STORAGE_MONITORING";
+
/** Type of car connection: platform runs directly in car. */
public static final int CONNECTION_TYPE_EMBEDDED = 5;
@@ -631,8 +669,13 @@
* only pass binder wrapper so that CarTestManager can be constructed outside. */
manager = new CarTestManagerBinderWrapper(binder);
break;
+ case VMS_SUBSCRIBER_SERVICE:
+ manager = new VmsSubscriberManager(binder, mEventHandler);
+ break;
case BLUETOOTH_SERVICE:
manager = new CarBluetoothManager(binder, mContext);
+ case STORAGE_MONITORING_SERVICE:
+ manager = new CarStorageMonitoringManager(binder);
}
return manager;
}
diff --git a/car-lib/src/android/car/diagnostic/CarDiagnosticManager.java b/car-lib/src/android/car/diagnostic/CarDiagnosticManager.java
index 84cdd3e..88980bf 100644
--- a/car-lib/src/android/car/diagnostic/CarDiagnosticManager.java
+++ b/car-lib/src/android/car/diagnostic/CarDiagnosticManager.java
@@ -349,8 +349,13 @@
}
/**
- * Returns true if this vehicle supports clearing freeze frame timestamps.
+ * Returns true if this vehicle supports clearing all freeze frames.
* This is only meaningful if freeze frame data is also supported.
+ *
+ * A return value of true for this method indicates that it is supported to call
+ * carDiagnosticManager.clearFreezeFrames()
+ * to delete all freeze frames stored in vehicle memory.
+ *
* @return
* @throws CarNotConnectedException
*/
@@ -365,6 +370,28 @@
return false;
}
+ /**
+ * Returns true if this vehicle supports clearing specific freeze frames by timestamp.
+ * This is only meaningful if freeze frame data is also supported.
+ *
+ * A return value of true for this method indicates that it is supported to call
+ * carDiagnosticManager.clearFreezeFrames(timestamp1, timestamp2, ...)
+ * to delete the freeze frames stored for the provided input timestamps, provided any exist.
+ *
+ * @return
+ * @throws CarNotConnectedException
+ */
+ public boolean isSelectiveClearFreezeFramesSupported() throws CarNotConnectedException {
+ try {
+ return mService.isSelectiveClearFreezeFramesSupported();
+ } catch (IllegalStateException e) {
+ CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException();
+ }
+ return false;
+ }
+
private static class CarDiagnosticEventListenerToService
extends Stub {
private final WeakReference<CarDiagnosticManager> mManager;
diff --git a/car-lib/src/android/car/diagnostic/ICarDiagnostic.aidl b/car-lib/src/android/car/diagnostic/ICarDiagnostic.aidl
index 3d1808f..57443d8 100644
--- a/car-lib/src/android/car/diagnostic/ICarDiagnostic.aidl
+++ b/car-lib/src/android/car/diagnostic/ICarDiagnostic.aidl
@@ -74,4 +74,10 @@
* Returns whether the underlying HAL supports clearing freeze frames.
*/
boolean isClearFreezeFramesSupported() = 10;
+
+ /**
+ * Returns whether the underlying HAL supports clearing specific freeze frames specified
+ * by means of their timestamps.
+ */
+ boolean isSelectiveClearFreezeFramesSupported() = 11;
}
diff --git a/car-lib/src/android/car/storagemonitoring/CarStorageMonitoringManager.java b/car-lib/src/android/car/storagemonitoring/CarStorageMonitoringManager.java
new file mode 100644
index 0000000..51d77d1
--- /dev/null
+++ b/car-lib/src/android/car/storagemonitoring/CarStorageMonitoringManager.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 android.car.storagemonitoring;
+
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.car.Car;
+import android.car.CarManagerBase;
+import android.car.CarNotConnectedException;
+import android.os.IBinder;
+import android.os.RemoteException;
+import java.util.Collections;
+import java.util.List;
+
+import static android.car.CarApiUtil.checkCarNotConnectedExceptionFromCarService;
+
+/**
+ * API for retrieving information and metrics about the flash storage.
+ *
+ * @hide
+ */
+@SystemApi
+public final class CarStorageMonitoringManager implements CarManagerBase {
+ private final ICarStorageMonitoring mService;
+
+ public static final int PRE_EOL_INFO_UNKNOWN = 0;
+ public static final int PRE_EOL_INFO_NORMAL = 1;
+ public static final int PRE_EOL_INFO_WARNING = 2;
+ public static final int PRE_EOL_INFO_URGENT = 3;
+
+ /**
+ * @hide
+ */
+ public CarStorageMonitoringManager(IBinder service) {
+ mService = ICarStorageMonitoring.Stub.asInterface(service);
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public void onCarDisconnected() {
+ }
+
+ // ICarStorageMonitoring forwards
+
+ /**
+ * This method returns the value of the "pre EOL" indicator for the flash storage
+ * as retrieved during the current boot cycle.
+ *
+ * It will return either PRE_EOL_INFO_UNKNOWN if the value can't be determined,
+ * or one of PRE_EOL_INFO_{NORMAL|WARNING|URGENT} depending on the device state.
+ */
+ @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING)
+ public int getPreEolIndicatorStatus() throws CarNotConnectedException {
+ try {
+ return mService.getPreEolIndicatorStatus();
+ } catch (IllegalStateException e) {
+ checkCarNotConnectedExceptionFromCarService(e);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException();
+ }
+ return PRE_EOL_INFO_UNKNOWN;
+ }
+
+ /**
+ * This method returns the value of the wear estimate indicators for the flash storage
+ * as retrieved during the current boot cycle.
+ *
+ * The indicators are guaranteed to be a lower-bound on the actual wear of the storage.
+ * Current technology in common automotive usage offers estimates in 10% increments.
+ *
+ * If either or both indicators are not available, they will be reported as UNKNOWN.
+ */
+ @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING)
+ public WearEstimate getWearEstimate() throws CarNotConnectedException {
+ try {
+ return mService.getWearEstimate();
+ } catch (IllegalStateException e) {
+ checkCarNotConnectedExceptionFromCarService(e);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException();
+ }
+ return WearEstimate.UNKNOWN_ESTIMATE;
+ }
+
+ /**
+ * This method returns a list of all changes in wear estimate indicators detected during the
+ * lifetime of the system.
+ *
+ * The indicators are not guaranteed to persist across a factory reset.
+ *
+ * The indicators are guaranteed to be a lower-bound on the actual wear of the storage.
+ * Current technology in common automotive usage offers estimates in 10% increments.
+ *
+ * If no indicators are available, an empty list will be returned.
+ */
+ @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING)
+ public List<WearEstimateChange> getWearEstimateHistory() throws CarNotConnectedException {
+ try {
+ return mService.getWearEstimateHistory();
+ } catch (IllegalStateException e) {
+ checkCarNotConnectedExceptionFromCarService(e);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException();
+ }
+ return Collections.emptyList();
+ }
+
+ /**
+ * This method returns a list of per user-id I/O activity metrics as collected at the end of
+ * system boot.
+ *
+ * The BOOT_COMPLETE broadcast is used as the trigger to collect this data. The implementation
+ * may impose an additional, and even variable across boot cycles, delay between the sending
+ * of the broadcast and the collection of the data.
+ *
+ * If the information is not available, an empty list will be returned.
+ */
+ @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING)
+ public List<UidIoStats> getBootIoStats() throws CarNotConnectedException {
+ try {
+ return mService.getBootIoStats();
+ } catch (IllegalStateException e) {
+ checkCarNotConnectedExceptionFromCarService(e);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException();
+ }
+ return Collections.emptyList();
+ }
+
+ /**
+ * This method returns a list of per user-id I/O activity metrics as collected from kernel
+ * start until the last snapshot.
+ *
+ * The samples provided might be as old as the value of the ioStatsRefreshRateSeconds setting.
+ *
+ * If the information is not available, an empty list will be returned.
+ */
+ @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING)
+ public List<UidIoStats> getAggregateIoStats() throws CarNotConnectedException {
+ try {
+ return mService.getAggregateIoStats();
+ } catch (IllegalStateException e) {
+ checkCarNotConnectedExceptionFromCarService(e);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException();
+ }
+ return Collections.emptyList();
+ }
+
+}
diff --git a/car-lib/src/android/car/storagemonitoring/ICarStorageMonitoring.aidl b/car-lib/src/android/car/storagemonitoring/ICarStorageMonitoring.aidl
new file mode 100644
index 0000000..cedb343
--- /dev/null
+++ b/car-lib/src/android/car/storagemonitoring/ICarStorageMonitoring.aidl
@@ -0,0 +1,50 @@
+/*
+ * 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.storagemonitoring;
+
+import android.car.storagemonitoring.UidIoStats;
+import android.car.storagemonitoring.WearEstimate;
+import android.car.storagemonitoring.WearEstimateChange;
+
+/** @hide */
+interface ICarStorageMonitoring {
+ /**
+ * Returns the value of the PRE_EOL register.
+ */
+ int getPreEolIndicatorStatus() = 1;
+
+ /**
+ * Returns the current wear estimate indicators.
+ */
+ WearEstimate getWearEstimate() = 2;
+
+ /**
+ * Returns the list of all observed wear estimate changes.
+ */
+ List<WearEstimateChange> getWearEstimateHistory() = 3;
+
+ /**
+ * Returns I/O stats as collected at service boot time.
+ */
+ List<UidIoStats> getBootIoStats() = 4;
+
+ /**
+ * Returns total I/O stats as collected from kernel start until the last snapshot.
+ */
+ List<UidIoStats> getAggregateIoStats() = 5;
+
+}
diff --git a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl b/car-lib/src/android/car/storagemonitoring/UidIoStats.aidl
similarity index 67%
copy from libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
copy to car-lib/src/android/car/storagemonitoring/UidIoStats.aidl
index ca0ac83..2a9aee1 100644
--- a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
+++ b/car-lib/src/android/car/storagemonitoring/UidIoStats.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,12 +14,6 @@
* limitations under the License.
*/
-package com.android.car.vehiclemonitor;
+package android.car.storagemonitoring;
-/**
- * Listener for vehicle monitor service.
- * @hide
- */
-oneway interface IVehicleMonitorListener {
- void onAppViolation(int pid, int uid, int action, int violation) = 0;
-}
+parcelable UidIoStats;
diff --git a/car-lib/src/android/car/storagemonitoring/UidIoStats.java b/car-lib/src/android/car/storagemonitoring/UidIoStats.java
new file mode 100644
index 0000000..3b8ae93
--- /dev/null
+++ b/car-lib/src/android/car/storagemonitoring/UidIoStats.java
@@ -0,0 +1,335 @@
+/*
+ * 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.storagemonitoring;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.JsonWriter;
+import java.io.IOException;
+import java.util.Objects;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * uid_io stats about one user ID.
+ *
+ * Contains information about I/O activity that can be attributed to processes running on
+ * behalf of one user of the system, as collected by the kernel.
+ *
+ * @hide
+ */
+@SystemApi
+public final class UidIoStats implements Parcelable {
+
+ public static final Parcelable.Creator<UidIoStats> CREATOR =
+ new Parcelable.Creator<UidIoStats>() {
+ public UidIoStats createFromParcel(Parcel in) {
+ return new UidIoStats(in);
+ }
+
+ public UidIoStats[] newArray(int size) {
+ return new UidIoStats[size];
+ }
+ };
+
+ /**
+ * The user id that this object contains metrics for.
+ *
+ * In many cases this can be converted to a list of Java app packages installed on the device.
+ * In other cases, the user id can refer to either the kernel itself (uid 0), or low-level
+ * system services that are running entirely natively.
+ */
+ public final int uid;
+
+ /**
+ * How long any process running on behalf of this user id running for, in milliseconds.
+ *
+ * This field is allowed to be an approximation and it does not provide any way to
+ * relate uptime to specific processes.
+ */
+ public final long runtimeMillis;
+
+ /**
+ * Statistics for apps running in foreground.
+ */
+ public final PerStateMetrics foreground;
+
+ /**
+ * Statistics for apps running in background.
+ */
+ public final PerStateMetrics background;
+
+ public UidIoStats(int uid,
+ long runtimeMillis, PerStateMetrics foreground, PerStateMetrics background) {
+ this.uid = uid;
+ this.runtimeMillis = runtimeMillis;
+ this.foreground = Objects.requireNonNull(foreground);
+ this.background = Objects.requireNonNull(background);
+ }
+
+ public UidIoStats(Parcel in) {
+ uid = in.readInt();
+ runtimeMillis = in.readLong();
+ foreground = in.readParcelable(PerStateMetrics.class.getClassLoader());
+ background = in.readParcelable(PerStateMetrics.class.getClassLoader());
+ }
+
+ public UidIoStats(UidIoStatsRecord record, long runtimeMillis) {
+ uid = record.uid;
+ this.runtimeMillis = runtimeMillis;
+ foreground = new PerStateMetrics(record.foreground_rchar,
+ record.foreground_wchar,
+ record.foreground_read_bytes,
+ record.foreground_write_bytes,
+ record.foreground_fsync);
+ background = new PerStateMetrics(record.background_rchar,
+ record.background_wchar,
+ record.background_read_bytes,
+ record.background_write_bytes,
+ record.background_fsync);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(uid);
+ dest.writeLong(runtimeMillis);
+ dest.writeParcelable(foreground, flags);
+ dest.writeParcelable(background, flags);
+ }
+
+ public void writeToJson(JsonWriter jsonWriter) throws IOException {
+ jsonWriter.beginObject();
+ jsonWriter.name("uid").value(uid);
+ jsonWriter.name("runtimeMillis").value(runtimeMillis);
+ jsonWriter.name("foreground"); foreground.writeToJson(jsonWriter);
+ jsonWriter.name("background"); background.writeToJson(jsonWriter);
+ jsonWriter.endObject();
+ }
+
+ public UidIoStats(JSONObject in) throws JSONException {
+ uid = in.getInt("uid");
+ runtimeMillis = in.getLong("runtimeMillis");
+ foreground = new PerStateMetrics(in.getJSONObject("foreground"));
+ background = new PerStateMetrics(in.getJSONObject("background"));
+ }
+
+ /**
+ * Returns the difference between the values stored in this object vs. those
+ * stored in other.
+ *
+ * It is the same as doing a delta() on foreground and background, plus verifying that
+ * both objects refer to the same uid.
+ *
+ * @hide
+ */
+ public UidIoStats delta(UidIoStats other) {
+ if (uid != other.uid) {
+ throw new IllegalArgumentException("cannot calculate delta between different user IDs");
+ }
+ return new UidIoStats(uid,
+ runtimeMillis - other.runtimeMillis,
+ foreground.delta(other.foreground), background.delta(other.background));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof UidIoStats) {
+ UidIoStats uidIoStatEntry = (UidIoStats)other;
+
+ return uid == uidIoStatEntry.uid &&
+ runtimeMillis == uidIoStatEntry.runtimeMillis &&
+ foreground.equals(uidIoStatEntry.foreground) &&
+ background.equals(uidIoStatEntry.background);
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uid, runtimeMillis, foreground, background);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("uid = %d, runtime = %d, foreground = %s, background = %s",
+ uid, runtimeMillis, foreground, background);
+ }
+
+ /**
+ * Validates that this object contains the same I/O metrics as a UidIoStatsRecord.
+ *
+ * It matches UID, and I/O activity values, but ignores runtime.
+ * @hide
+ */
+ public boolean representsSameMetrics(UidIoStatsRecord record) {
+ return record.uid == uid &&
+ record.foreground_rchar == foreground.bytesRead &&
+ record.foreground_wchar == foreground.bytesWritten &&
+ record.foreground_read_bytes == foreground.bytesReadFromStorage &&
+ record.foreground_write_bytes == foreground.bytesWrittenToStorage &&
+ record.foreground_fsync == foreground.fsyncCalls &&
+ record.background_rchar == background.bytesRead &&
+ record.background_wchar == background.bytesWritten &&
+ record.background_read_bytes == background.bytesReadFromStorage &&
+ record.background_write_bytes == background.bytesWrittenToStorage &&
+ record.background_fsync == background.fsyncCalls;
+ }
+
+ /**
+ * I/O activity metrics that pertain to either the foreground or the background state.
+ */
+ public static final class PerStateMetrics implements Parcelable {
+
+ public static final Parcelable.Creator<PerStateMetrics> CREATOR =
+ new Parcelable.Creator<PerStateMetrics>() {
+ public PerStateMetrics createFromParcel(Parcel in) {
+ return new PerStateMetrics(in);
+ }
+
+ public PerStateMetrics[] newArray(int size) {
+ return new PerStateMetrics[size];
+ }
+ };
+
+ /**
+ * Total bytes that processes running on behalf of this user obtained
+ * via read() system calls.
+ */
+ public final long bytesRead;
+
+ /**
+ * Total bytes that processes running on behalf of this user transferred
+ * via write() system calls.
+ */
+ public final long bytesWritten;
+
+ /**
+ * Total bytes that processes running on behalf of this user obtained
+ * via read() system calls that actually were served by physical storage.
+ */
+ public final long bytesReadFromStorage;
+
+ /**
+ * Total bytes that processes running on behalf of this user transferred
+ * via write() system calls that were actually sent to physical storage.
+ */
+ public final long bytesWrittenToStorage;
+
+ /**
+ * Total number of fsync() system calls that processes running on behalf of this user made.
+ */
+ public final long fsyncCalls;
+
+ public PerStateMetrics(long bytesRead, long bytesWritten, long bytesReadFromStorage,
+ long bytesWrittenToStorage, long fsyncCalls) {
+ this.bytesRead = bytesRead;
+ this.bytesWritten = bytesWritten;
+ this.bytesReadFromStorage = bytesReadFromStorage;
+ this.bytesWrittenToStorage = bytesWrittenToStorage;
+ this.fsyncCalls = fsyncCalls;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(bytesRead);
+ dest.writeLong(bytesWritten);
+ dest.writeLong(bytesReadFromStorage);
+ dest.writeLong(bytesWrittenToStorage);
+ dest.writeLong(fsyncCalls);
+ }
+
+ public void writeToJson(JsonWriter jsonWriter) throws IOException {
+ jsonWriter.beginObject();
+ jsonWriter.name("bytesRead").value(bytesRead);
+ jsonWriter.name("bytesWritten").value(bytesWritten);
+ jsonWriter.name("bytesReadFromStorage").value(bytesReadFromStorage);
+ jsonWriter.name("bytesWrittenToStorage").value(bytesWrittenToStorage);
+ jsonWriter.name("fsyncCalls").value(fsyncCalls);
+ jsonWriter.endObject();
+ }
+
+ public PerStateMetrics(Parcel in) {
+ bytesRead = in.readLong();
+ bytesWritten = in.readLong();
+ bytesReadFromStorage = in.readLong();
+ bytesWrittenToStorage = in.readLong();
+ fsyncCalls = in.readLong();
+ }
+
+ public PerStateMetrics(JSONObject in) throws JSONException {
+ bytesRead = in.getLong("bytesRead");
+ bytesWritten = in.getLong("bytesWritten");
+ bytesReadFromStorage = in.getLong("bytesReadFromStorage");
+ bytesWrittenToStorage = in.getLong("bytesWrittenToStorage");
+ fsyncCalls = in.getLong("fsyncCalls");
+ }
+
+ /**
+ * Computes the difference between the values stored in this object
+ * vs. those stored in other
+ *
+ * It is the same as doing
+ * new PerStateMetrics(bytesRead-other.bytesRead,bytesWritten-other.bytesWritten, ...)
+ *
+ * @hide
+ */
+ public PerStateMetrics delta(PerStateMetrics other) {
+ return new PerStateMetrics(bytesRead-other.bytesRead,
+ bytesWritten-other.bytesWritten,
+ bytesReadFromStorage-other.bytesReadFromStorage,
+ bytesWrittenToStorage-other.bytesWrittenToStorage,
+ fsyncCalls-other.fsyncCalls);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof PerStateMetrics) {
+ PerStateMetrics perStateMetrics = (PerStateMetrics)other;
+
+ return (bytesRead == perStateMetrics.bytesRead) &&
+ (bytesWritten == perStateMetrics.bytesWritten) &&
+ (bytesReadFromStorage == perStateMetrics.bytesReadFromStorage) &&
+ (bytesWrittenToStorage == perStateMetrics.bytesWrittenToStorage) &&
+ (fsyncCalls == perStateMetrics.fsyncCalls);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(bytesRead, bytesWritten, bytesReadFromStorage,
+ bytesWrittenToStorage, fsyncCalls);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("bytesRead=%d, bytesWritten=%d, bytesReadFromStorage=%d, bytesWrittenToStorage=%d, fsyncCalls=%d",
+ bytesRead, bytesWritten, bytesReadFromStorage, bytesWrittenToStorage, fsyncCalls);
+ }
+ }
+}
diff --git a/car-lib/src/android/car/storagemonitoring/UidIoStatsRecord.java b/car-lib/src/android/car/storagemonitoring/UidIoStatsRecord.java
new file mode 100644
index 0000000..abd9ee0
--- /dev/null
+++ b/car-lib/src/android/car/storagemonitoring/UidIoStatsRecord.java
@@ -0,0 +1,86 @@
+/*
+ * 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.storagemonitoring;
+
+import android.annotation.SystemApi;
+
+/**
+ * Record of data as extracted from /proc/uid_io/stats
+ *
+ * @hide
+ */
+@SystemApi
+public final class UidIoStatsRecord {
+
+ public final int uid;
+
+ public final long foreground_rchar;
+ public final long foreground_wchar;
+ public final long foreground_read_bytes;
+ public final long foreground_write_bytes;
+ public final long foreground_fsync;
+
+ public final long background_rchar;
+ public final long background_wchar;
+ public final long background_read_bytes;
+ public final long background_write_bytes;
+ public final long background_fsync;
+
+ public UidIoStatsRecord(int uid,
+ long foreground_rchar,
+ long foreground_wchar,
+ long foreground_read_bytes,
+ long foreground_write_bytes,
+ long foreground_fsync,
+ long background_rchar,
+ long background_wchar,
+ long background_read_bytes,
+ long background_write_bytes,
+ long background_fsync) {
+ this.uid = uid;
+
+ this.foreground_rchar = foreground_rchar;
+ this.foreground_wchar = foreground_wchar;
+ this.foreground_read_bytes = foreground_read_bytes;
+ this.foreground_write_bytes = foreground_write_bytes;
+ this.foreground_fsync = foreground_fsync;
+
+ this.background_rchar = background_rchar;
+ this.background_wchar = background_wchar;
+ this.background_read_bytes = background_read_bytes;
+ this.background_write_bytes = background_write_bytes;
+ this.background_fsync = background_fsync;
+ }
+
+ /** @hide */
+ public UidIoStatsRecord delta(UidIoStats other) {
+ if (uid != other.uid) {
+ throw new IllegalArgumentException("cannot calculate delta between different user IDs");
+ }
+
+ return new UidIoStatsRecord(uid,
+ foreground_rchar - other.foreground.bytesRead,
+ foreground_wchar - other.foreground.bytesWritten,
+ foreground_read_bytes - other.foreground.bytesReadFromStorage,
+ foreground_write_bytes - other.foreground.bytesWrittenToStorage,
+ foreground_fsync - other.foreground.fsyncCalls,
+ background_rchar - other.background.bytesRead,
+ background_wchar - other.background.bytesWritten,
+ background_read_bytes - other.background.bytesReadFromStorage,
+ background_write_bytes - other.background.bytesWrittenToStorage,
+ background_fsync - other.background.fsyncCalls);
+ }
+}
diff --git a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl b/car-lib/src/android/car/storagemonitoring/WearEstimate.aidl
similarity index 67%
copy from libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
copy to car-lib/src/android/car/storagemonitoring/WearEstimate.aidl
index ca0ac83..c7ea505 100644
--- a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
+++ b/car-lib/src/android/car/storagemonitoring/WearEstimate.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,12 +14,6 @@
* limitations under the License.
*/
-package com.android.car.vehiclemonitor;
+package android.car.storagemonitoring;
-/**
- * Listener for vehicle monitor service.
- * @hide
- */
-oneway interface IVehicleMonitorListener {
- void onAppViolation(int pid, int uid, int action, int violation) = 0;
-}
+parcelable WearEstimate;
diff --git a/car-lib/src/android/car/storagemonitoring/WearEstimate.java b/car-lib/src/android/car/storagemonitoring/WearEstimate.java
new file mode 100644
index 0000000..507e6ed
--- /dev/null
+++ b/car-lib/src/android/car/storagemonitoring/WearEstimate.java
@@ -0,0 +1,152 @@
+/*
+ * 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.storagemonitoring;
+
+import android.annotation.IntRange;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.JsonReader;
+import android.util.JsonWriter;
+import java.io.IOException;
+import java.util.Objects;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Wear-out information for flash storage.
+ *
+ * Contains a lower-bound estimate of the wear of "type A" (SLC) and "type B" (MLC) storage.
+ *
+ * Current technology offers wear data in increments of 10% (i.e. from 0 == new device up to
+ * 100 == worn-out device). It is possible for a storage device to only support one type of memory
+ * cell, in which case it is expected that the storage type not supported will have UNKNOWN wear.
+ *
+ * @hide
+ */
+@SystemApi
+public class WearEstimate implements Parcelable {
+ public static final int UNKNOWN = -1;
+
+ /** @hide */
+ public static final WearEstimate UNKNOWN_ESTIMATE = new WearEstimate(UNKNOWN, UNKNOWN);
+
+ public static final Parcelable.Creator<WearEstimate> CREATOR =
+ new Parcelable.Creator<WearEstimate>() {
+ public WearEstimate createFromParcel(Parcel in) {
+ return new WearEstimate(in);
+ }
+
+ public WearEstimate[] newArray(int size) {
+ return new WearEstimate[size];
+ }
+ };
+
+ /**
+ * Wear estimate data for "type A" storage.
+ */
+ @IntRange(from=-1, to=100)
+ public final int typeA;
+
+ /**
+ * Wear estimate data for "type B" storage.
+ */
+ @IntRange(from=-1, to=100)
+ public final int typeB;
+
+ private static final int validateWearValue(int value) {
+ if (value == UNKNOWN) return value;
+ if ((value >= 0) && (value <= 100)) return value;
+ throw new IllegalArgumentException(value + " is not a valid wear estimate");
+ }
+
+ public WearEstimate(int typeA, int typeB) {
+ this.typeA = validateWearValue(typeA);
+ this.typeB = validateWearValue(typeB);
+ }
+
+ public WearEstimate(Parcel in) {
+ typeA = validateWearValue(in.readInt());
+ typeB = validateWearValue(in.readInt());
+ }
+
+ public WearEstimate(JsonReader in) throws IOException {
+ int typeA = UNKNOWN;
+ int typeB = UNKNOWN;
+ in.beginObject();
+ while (in.hasNext()) {
+ switch (in.nextName()) {
+ case "wearEstimateTypeA":
+ typeA = validateWearValue(in.nextInt());
+ break;
+ case "wearEstimateTypeB":
+ typeB = validateWearValue(in.nextInt());
+ break;
+ }
+ }
+ in.endObject();
+ this.typeA = typeA;
+ this.typeB = typeB;
+ }
+
+ /** @hide */
+ public WearEstimate(JSONObject in) throws JSONException {
+ typeA = in.getInt("wearEstimateTypeA");
+ typeB = in.getInt("wearEstimateTypeB");
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(typeA);
+ dest.writeInt(typeB);
+ }
+
+ public void writeToJson(JsonWriter jsonWriter) throws IOException {
+ jsonWriter.beginObject();
+ jsonWriter.name("wearEstimateTypeA").value(typeA);
+ jsonWriter.name("wearEstimateTypeB").value(typeB);
+ jsonWriter.endObject();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof WearEstimate) {
+ WearEstimate wo = (WearEstimate)other;
+ return wo.typeA == typeA && wo.typeB == typeB;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(typeA, typeB);
+ }
+
+ private static final String wearValueToString(int value) {
+ if (value == UNKNOWN) return "unknown";
+ return value + "%";
+ }
+
+ @Override
+ public String toString() {
+ return "type A: " + wearValueToString(typeA) + ", type B: " + wearValueToString(typeB);
+ }
+}
diff --git a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl b/car-lib/src/android/car/storagemonitoring/WearEstimateChange.aidl
similarity index 67%
copy from libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
copy to car-lib/src/android/car/storagemonitoring/WearEstimateChange.aidl
index ca0ac83..49adfe5 100644
--- a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
+++ b/car-lib/src/android/car/storagemonitoring/WearEstimateChange.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,12 +14,6 @@
* limitations under the License.
*/
-package com.android.car.vehiclemonitor;
+package android.car.storagemonitoring;
-/**
- * Listener for vehicle monitor service.
- * @hide
- */
-oneway interface IVehicleMonitorListener {
- void onAppViolation(int pid, int uid, int action, int violation) = 0;
-}
+parcelable WearEstimateChange;
diff --git a/car-lib/src/android/car/storagemonitoring/WearEstimateChange.java b/car-lib/src/android/car/storagemonitoring/WearEstimateChange.java
new file mode 100644
index 0000000..5c80a31
--- /dev/null
+++ b/car-lib/src/android/car/storagemonitoring/WearEstimateChange.java
@@ -0,0 +1,143 @@
+/*
+ * 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.storagemonitoring;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import java.time.Instant;
+import java.util.Objects;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Change in wear-out information.
+ *
+ * Contains information about the first cycle during which the system detected a change
+ * in wear-out information of the flash storage.
+ *
+ * @hide
+ */
+@SystemApi
+public class WearEstimateChange implements Parcelable {
+ public static final Parcelable.Creator<WearEstimateChange> CREATOR =
+ new Parcelable.Creator<WearEstimateChange>() {
+ public WearEstimateChange createFromParcel(Parcel in) {
+ return new WearEstimateChange(in);
+ }
+
+ public WearEstimateChange[] newArray(int size) {
+ return new WearEstimateChange[size];
+ }
+ };
+
+ /**
+ * The previous wear estimate.
+ */
+ public final @NonNull WearEstimate oldEstimate;
+
+ /**
+ * The new wear estimate.
+ */
+ public final @NonNull WearEstimate newEstimate;
+
+ /**
+ * Total CarService uptime when this change was detected.
+ */
+ public final long uptimeAtChange;
+
+ /**
+ * Wall-clock time when this change was detected.
+ */
+ public final @NonNull Instant dateAtChange;
+
+ /**
+ * Whether this change was within the vendor range for acceptable flash degradation.
+ */
+ public final boolean isAcceptableDegradation;
+
+ public WearEstimateChange(WearEstimate oldEstimate,
+ WearEstimate newEstimate,
+ long uptimeAtChange,
+ Instant dateAtChange,
+ boolean isAcceptableDegradation) {
+ if (uptimeAtChange < 0) {
+ throw new IllegalArgumentException("uptimeAtChange must be >= 0");
+ }
+ this.oldEstimate = requireNonNull(oldEstimate);
+ this.newEstimate = requireNonNull(newEstimate);
+ this.uptimeAtChange = uptimeAtChange;
+ this.dateAtChange = requireNonNull(dateAtChange);
+ this.isAcceptableDegradation = isAcceptableDegradation;
+ }
+
+ public WearEstimateChange(Parcel in) {
+ oldEstimate = in.readParcelable(WearEstimate.class.getClassLoader());
+ newEstimate = in.readParcelable(WearEstimate.class.getClassLoader());
+ uptimeAtChange = in.readLong();
+ dateAtChange = Instant.ofEpochMilli(in.readLong());
+ isAcceptableDegradation = in.readInt() == 1;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(oldEstimate, flags);
+ dest.writeParcelable(newEstimate, flags);
+ dest.writeLong(uptimeAtChange);
+ dest.writeLong(dateAtChange.toEpochMilli());
+ dest.writeInt(isAcceptableDegradation ? 1 : 0);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof WearEstimateChange) {
+ WearEstimateChange wo = (WearEstimateChange)other;
+ return wo.isAcceptableDegradation == isAcceptableDegradation &&
+ wo.uptimeAtChange == uptimeAtChange &&
+ wo.dateAtChange.equals(dateAtChange) &&
+ wo.oldEstimate.equals(oldEstimate) &&
+ wo.newEstimate.equals(newEstimate);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(oldEstimate,
+ newEstimate,
+ uptimeAtChange,
+ dateAtChange,
+ isAcceptableDegradation);
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "wear change{old level=%s, new level=%s, uptime=%d, date=%s, acceptable=%s}",
+ oldEstimate,
+ newEstimate,
+ uptimeAtChange,
+ dateAtChange,
+ isAcceptableDegradation ? "yes" : "no");
+ }
+
+}
diff --git a/car-lib/src/android/car/vms/IVmsPublisherClient.aidl b/car-lib/src/android/car/vms/IVmsPublisherClient.aidl
new file mode 100644
index 0000000..96b993b
--- /dev/null
+++ b/car-lib/src/android/car/vms/IVmsPublisherClient.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.vms.IVmsPublisherService;
+import android.car.vms.VmsSubscriptionState;
+
+/**
+ * @hide
+ */
+interface IVmsPublisherClient {
+ /**
+ * Once the VmsPublisherService is bound to the client, this callback is used to set the
+ * binder that the client can use to invoke publisher services. This also gives the client
+ * the token it should use when calling the service.
+ */
+ oneway void setVmsPublisherService(in IBinder token, IVmsPublisherService service) = 0;
+
+ /**
+ * The VmsPublisherService uses this callback to notify about subscription changes.
+ * @param subscriptionState all the layers that have subscribers and a sequence number,
+ * clients should ignore any packet with a sequence number that is less
+ * than the highest sequence number they have seen thus far.
+ */
+ oneway void onVmsSubscriptionChange(in VmsSubscriptionState subscriptionState) = 1;
+}
diff --git a/car-lib/src/android/car/vms/IVmsPublisherService.aidl b/car-lib/src/android/car/vms/IVmsPublisherService.aidl
new file mode 100644
index 0000000..3c26a1b
--- /dev/null
+++ b/car-lib/src/android/car/vms/IVmsPublisherService.aidl
@@ -0,0 +1,50 @@
+/*
+ * 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.vms.VmsLayer;
+import android.car.vms.VmsLayersOffering;
+import android.car.vms.VmsSubscriptionState;
+
+/**
+ * Exposes publisher services to VMS clients.
+ *
+ * @hide
+ */
+interface IVmsPublisherService {
+ /**
+ * Client call to publish a message.
+ */
+ 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.
+ */
+ VmsSubscriptionState getSubscriptions() = 1;
+
+ /**
+ * Sets which layers the publisher can publish under which dependencties.
+ */
+ oneway void setLayersOffering(in IBinder token, in VmsLayersOffering offering) = 2;
+
+ /**
+ * The first time a publisher calls this API it will store the publisher info and assigns the
+ * 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 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
new file mode 100644
index 0000000..d5b5606
--- /dev/null
+++ b/car-lib/src/android/car/vms/IVmsSubscriberClient.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.vms;
+
+import android.car.vms.VmsAssociatedLayer;
+import android.car.vms.VmsLayer;
+
+/**
+ * @hide
+ */
+oneway interface IVmsSubscriberClient {
+ /**
+ * A VmsService uses this callback to pass messages to subscribers.
+ */
+ void onVmsMessageReceived(in VmsLayer layer, in byte[] payload) = 0;
+
+ 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
new file mode 100644
index 0000000..d10a6e4
--- /dev/null
+++ b/car-lib/src/android/car/vms/IVmsSubscriberService.aidl
@@ -0,0 +1,96 @@
+/*
+ * 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.vms.IVmsSubscriberClient;
+import android.car.vms.VmsLayer;
+
+/**
+ * @hide
+ */
+interface IVmsSubscriberService {
+ /**
+ * 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 addVmsSubscriberToNotifications(
+ in IVmsSubscriberClient subscriber) = 0;
+
+ /**
+ * Adds a subscriber to a VMS layer.
+ */
+ void addVmsSubscriber(
+ in IVmsSubscriberClient subscriber,
+ in VmsLayer layer) = 1;
+
+ /**
+ * 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;
+
+ /**
+ * 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 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() = 8;
+
+ /**
+ * Returns a the publisher information for a publisher ID.
+ */
+ byte[] getPublisherInfo(in int publisherId) = 9;
+}
diff --git a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl b/car-lib/src/android/car/vms/VmsAssociatedLayer.aidl
similarity index 67%
copy from libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
copy to car-lib/src/android/car/vms/VmsAssociatedLayer.aidl
index ca0ac83..c2cf271 100644
--- a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
+++ b/car-lib/src/android/car/vms/VmsAssociatedLayer.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,12 +14,6 @@
* limitations under the License.
*/
-package com.android.car.vehiclemonitor;
+package android.car.vms;
-/**
- * Listener for vehicle monitor service.
- * @hide
- */
-oneway interface IVehicleMonitorListener {
- void onAppViolation(int pid, int uid, int action, int violation) = 0;
-}
+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..55ea541
--- /dev/null
+++ b/car-lib/src/android/car/vms/VmsAssociatedLayer.java
@@ -0,0 +1,99 @@
+/*
+ * 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.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.*;
+
+/**
+ * A VMS Layer with a list of publisher IDs it is associated with.
+ *
+ * @hide
+ */
+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/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl b/car-lib/src/android/car/vms/VmsLayer.aidl
similarity index 67%
copy from libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
copy to car-lib/src/android/car/vms/VmsLayer.aidl
index ca0ac83..ff0768a 100644
--- a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
+++ b/car-lib/src/android/car/vms/VmsLayer.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,12 +14,6 @@
* limitations under the License.
*/
-package com.android.car.vehiclemonitor;
+package android.car.vms;
-/**
- * Listener for vehicle monitor service.
- * @hide
- */
-oneway interface IVehicleMonitorListener {
- void onAppViolation(int pid, int uid, int action, int violation) = 0;
-}
+parcelable VmsLayer;
\ 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
new file mode 100644
index 0000000..b921230
--- /dev/null
+++ b/car-lib/src/android/car/vms/VmsLayer.java
@@ -0,0 +1,126 @@
+/*
+ * 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.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * A VMS Layer which can be subscribed to by VMS clients.
+ *
+ * @hide
+ */
+@SystemApi
+public final class VmsLayer implements Parcelable {
+
+ // The layer Type.
+ private int mType;
+
+ // The layer Subtype.
+ private int mSubtype;
+
+ // The layer version.
+ private int mVersion;
+
+ public VmsLayer(int type, int subtype, int version) {
+ mType = type;
+ mSubtype = subtype;
+ mVersion = version;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public int getSubtype() {
+ return mSubtype;
+ }
+
+ public int getVersion() {
+ return mVersion;
+ }
+
+ /**
+ * Checks the two objects for equality by comparing their IDs and Versions.
+ *
+ * @param o the {@link VmsLayer} to which this one is to be checked for equality
+ * @return true if the underlying objects of the VmsLayer are both considered equal
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof VmsLayer)) {
+ return false;
+ }
+ VmsLayer p = (VmsLayer) o;
+ return Objects.equals(p.mType, mType) &&
+ Objects.equals(p.mSubtype, mSubtype) &&
+ Objects.equals(p.mVersion, mVersion);
+ }
+
+ /**
+ * Compute a hash code similarly tp {@link android.util.Pair}
+ *
+ * @return a hashcode of the Pair
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(mType, mSubtype, mVersion);
+ }
+
+ @Override
+ public String toString() {
+ return "VmsLayer{ Type: " + mType + ", Sub type: " + mSubtype + ", Version: " + mVersion + "}";
+ }
+
+
+ // Parcelable related methods.
+ public static final Parcelable.Creator<VmsLayer> CREATOR = new
+ Parcelable.Creator<VmsLayer>() {
+ public VmsLayer createFromParcel(Parcel in) {
+ return new VmsLayer(in);
+ }
+
+ public VmsLayer[] newArray(int size) {
+ return new VmsLayer[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mType);
+ out.writeInt(mSubtype);
+ out.writeInt(mVersion);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private VmsLayer(Parcel in) {
+ readFromParcel(in);
+ }
+
+ private void readFromParcel(Parcel in) {
+ mType = in.readInt();
+ mSubtype = in.readInt();
+ mVersion = in.readInt();
+ }
+}
\ No newline at end of file
diff --git a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl b/car-lib/src/android/car/vms/VmsLayerDependency.aidl
similarity index 67%
copy from libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
copy to car-lib/src/android/car/vms/VmsLayerDependency.aidl
index ca0ac83..3e64001 100644
--- a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
+++ b/car-lib/src/android/car/vms/VmsLayerDependency.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,12 +14,6 @@
* limitations under the License.
*/
-package com.android.car.vehiclemonitor;
+package android.car.vms;
-/**
- * Listener for vehicle monitor service.
- * @hide
- */
-oneway interface IVehicleMonitorListener {
- void onAppViolation(int pid, int uid, int action, int violation) = 0;
-}
+parcelable VmsLayerDependency;
\ 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
new file mode 100644
index 0000000..869fc84
--- /dev/null
+++ b/car-lib/src/android/car/vms/VmsLayerDependency.java
@@ -0,0 +1,97 @@
+/*
+ * 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.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A dependency for a VMS layer on other VMS layers.
+ *
+ * @hide
+ */
+@SystemApi
+public final class VmsLayerDependency implements Parcelable {
+ private final VmsLayer mLayer;
+ private final Set<VmsLayer> mDependency;
+
+ /**
+ * Construct a dependency for layer on other layers.
+ */
+ public VmsLayerDependency(VmsLayer layer, Set<VmsLayer> dependencies) {
+ mLayer = layer;
+ mDependency = Collections.unmodifiableSet(dependencies);
+ }
+
+ /**
+ * Constructs a layer without a dependency.
+ */
+ public VmsLayerDependency(VmsLayer layer) {
+ mLayer = layer;
+ mDependency = Collections.emptySet();
+ }
+
+ public VmsLayer getLayer() {
+ return mLayer;
+ }
+
+ /**
+ * Returns the dependencies.
+ */
+ public Set<VmsLayer> getDependencies() {
+ return mDependency;
+ }
+
+ public static final Parcelable.Creator<VmsLayerDependency> CREATOR = new
+ Parcelable.Creator<VmsLayerDependency>() {
+ public VmsLayerDependency createFromParcel(Parcel in) {
+ return new VmsLayerDependency(in);
+ }
+ public VmsLayerDependency[] newArray(int size) {
+ return new VmsLayerDependency[size];
+ }
+ };
+
+ public String toString() {
+ return "VmsLayerDependency{ Layer: " + mLayer + " Dependency: " + mDependency + "}";
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(mLayer, flags);
+ out.writeParcelableList(new ArrayList<VmsLayer>(mDependency), flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private VmsLayerDependency(Parcel in) {
+ mLayer = in.readParcelable(VmsLayer.class.getClassLoader());
+ List<VmsLayer> dependency = new ArrayList<>();
+ in.readParcelableList(dependency, VmsLayer.class.getClassLoader());
+ mDependency = Collections.unmodifiableSet(new HashSet<VmsLayer>(dependency));
+ }
+}
\ No newline at end of file
diff --git a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl b/car-lib/src/android/car/vms/VmsLayersOffering.aidl
similarity index 67%
copy from libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
copy to car-lib/src/android/car/vms/VmsLayersOffering.aidl
index ca0ac83..4231f2d 100644
--- a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
+++ b/car-lib/src/android/car/vms/VmsLayersOffering.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,12 +14,6 @@
* limitations under the License.
*/
-package com.android.car.vehiclemonitor;
+package android.car.vms;
-/**
- * Listener for vehicle monitor service.
- * @hide
- */
-oneway interface IVehicleMonitorListener {
- void onAppViolation(int pid, int uid, int action, int violation) = 0;
-}
+parcelable VmsLayersOffering;
\ No newline at end of file
diff --git a/car-lib/src/android/car/vms/VmsLayersOffering.java b/car-lib/src/android/car/vms/VmsLayersOffering.java
new file mode 100644
index 0000000..fc83696
--- /dev/null
+++ b/car-lib/src/android/car/vms/VmsLayersOffering.java
@@ -0,0 +1,89 @@
+/*
+ * 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.annotation.SystemApi;
+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 state of dependencies for a single publisher.
+ *
+ * @hide
+ */
+@SystemApi
+public final class VmsLayersOffering implements Parcelable {
+
+ private final Set<VmsLayerDependency> mDependencies;
+
+ private final int mPublisherId;
+
+ public VmsLayersOffering(Set<VmsLayerDependency> dependencies, int publisherId) {
+ mDependencies = Collections.unmodifiableSet(dependencies);
+ mPublisherId = publisherId;
+ }
+
+ /**
+ * Returns the dependencies.
+ */
+ 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) {
+ return new VmsLayersOffering(in);
+ }
+ public VmsLayersOffering[] newArray(int size) {
+ return new VmsLayersOffering[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "VmsLayersOffering{" + mDependencies+ "}";
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+
+ out.writeParcelableList(new ArrayList(mDependencies), flags);
+ out.writeInt(mPublisherId);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private VmsLayersOffering(Parcel in) {
+ List<VmsLayerDependency> dependencies = new ArrayList<>();
+ in.readParcelableList(dependencies, VmsLayerDependency.class.getClassLoader());
+ 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..bfb6055
--- /dev/null
+++ b/car-lib/src/android/car/vms/VmsOperationRecorder.java
@@ -0,0 +1,227 @@
+package android.car.vms;
+
+import android.annotation.SystemApi;
+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
+ */
+@SystemApi
+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
new file mode 100644
index 0000000..024fc14
--- /dev/null
+++ b/car-lib/src/android/car/vms/VmsPublisherClientService.java
@@ -0,0 +1,282 @@
+/*
+ * 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.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.annotation.Nullable;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Services that need VMS publisher services need to inherit from this class and also need to be
+ * declared in the array vmsPublisherClients located in
+ * packages/services/Car/service/res/values/config.xml (most likely, this file will be in an overlay
+ * of the target product.
+ *
+ * The {@link com.android.car.VmsPublisherService} will start this service. The callback
+ * {@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.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class VmsPublisherClientService extends Service {
+ private static final boolean DBG = true;
+ private static final String TAG = "VmsPublisherClient";
+
+ private final Object mLock = new Object();
+
+ private Handler mHandler = new VmsEventHandler(this);
+ private final VmsPublisherClientBinder mVmsPublisherClient = new VmsPublisherClientBinder(this);
+ private volatile IVmsPublisherService mVmsPublisherService = null;
+ @GuardedBy("mLock")
+ private IBinder mToken = null;
+
+ @Override
+ public final IBinder onBind(Intent intent) {
+ if (DBG) {
+ Log.d(TAG, "onBind, intent: " + intent);
+ }
+ return mVmsPublisherClient.asBinder();
+ }
+
+ @Override
+ public final boolean onUnbind(Intent intent) {
+ if (DBG) {
+ Log.d(TAG, "onUnbind, intent: " + intent);
+ }
+ stopSelf();
+ return super.onUnbind(intent);
+ }
+
+ private void setToken(IBinder token) {
+ synchronized (mLock) {
+ mToken = token;
+ }
+ }
+
+ /**
+ * Notifies that the publisher services are ready.
+ */
+ protected abstract void onVmsPublisherServiceReady();
+
+ /**
+ * Publishers need to implement this method to receive notifications of subscription changes.
+ *
+ * @param subscriptionState the state of the subscriptions.
+ */
+ public abstract void onVmsSubscriptionChange(VmsSubscriptionState subscriptionState);
+
+ /**
+ * Uses the VmsPublisherService binder to publish messages.
+ *
+ * @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 void publish(VmsLayer layer, int publisherId, byte[] payload) {
+ if (DBG) {
+ Log.d(TAG, "Publishing for layer : " + layer);
+ }
+
+ IBinder token = getTokenForPublisherServiceThreadSafe();
+
+ try {
+ mVmsPublisherService.publish(token, layer, publisherId, payload);
+ } catch (RemoteException e) {
+ Log.e(TAG, "unable to publish message: " + payload, e);
+ }
+ }
+
+ /**
+ * Uses the VmsPublisherService binder to set the layers offering.
+ *
+ * @param offering the layers that the publisher may publish.
+ * @return if the call to VmsPublisherService.setLayersOffering was successful.
+ */
+ public final void setLayersOffering(VmsLayersOffering offering) {
+ if (DBG) {
+ Log.d(TAG, "Setting layers offering : " + offering);
+ }
+
+ IBinder token = getTokenForPublisherServiceThreadSafe();
+
+ try {
+ mVmsPublisherService.setLayersOffering(token, offering);
+ VmsOperationRecorder.get().setLayersOffering(offering);
+ } catch (RemoteException e) {
+ Log.e(TAG, "unable to set layers offering: " + offering, e);
+ }
+ }
+
+ private IBinder getTokenForPublisherServiceThreadSafe() {
+ if (mVmsPublisherService == null) {
+ throw new IllegalStateException("VmsPublisherService not set.");
+ }
+
+ IBinder token;
+ synchronized (mLock) {
+ token = mToken;
+ }
+ if (token == null) {
+ throw new IllegalStateException("VmsPublisherService does not have a valid token.");
+ }
+ return token;
+ }
+
+ public final int getPublisherId(byte[] publisherInfo) {
+ if (mVmsPublisherService == null) {
+ throw new IllegalStateException("VmsPublisherService not set.");
+ }
+ Integer publisherId = null;
+ try {
+ Log.i(TAG, "Getting publisher static ID");
+ publisherId = mVmsPublisherService.getPublisherId(publisherInfo);
+ } catch (RemoteException e) {
+ Log.e(TAG, "unable to invoke binder method.", e);
+ }
+ if (publisherId == null) {
+ throw new IllegalStateException("VmsPublisherService cannot get a publisher static ID.");
+ } else {
+ VmsOperationRecorder.get().getPublisherId(publisherId);
+ }
+ return publisherId;
+ }
+
+ /**
+ * Uses the VmsPublisherService binder to get the state of the subscriptions.
+ *
+ * @return list of layer/version or null in case of error.
+ */
+ public final @Nullable VmsSubscriptionState getSubscriptions() {
+ if (mVmsPublisherService == null) {
+ throw new IllegalStateException("VmsPublisherService not set.");
+ }
+ try {
+ return mVmsPublisherService.getSubscriptions();
+ } catch (RemoteException e) {
+ Log.e(TAG, "unable to invoke binder method.", e);
+ }
+ return null;
+ }
+
+ private void setVmsPublisherService(IVmsPublisherService service) {
+ mVmsPublisherService = service;
+ onVmsPublisherServiceReady();
+ }
+
+ /**
+ * Implements the interface that the VMS service uses to communicate with this client.
+ */
+ private static class VmsPublisherClientBinder extends IVmsPublisherClient.Stub {
+ private final WeakReference<VmsPublisherClientService> mVmsPublisherClientService;
+ @GuardedBy("mSequenceLock")
+ private long mSequence = -1;
+ private final Object mSequenceLock = new Object();
+
+ public VmsPublisherClientBinder(VmsPublisherClientService vmsPublisherClientService) {
+ mVmsPublisherClientService = new WeakReference<>(vmsPublisherClientService);
+ }
+
+ @Override
+ public void setVmsPublisherService(IBinder token, IVmsPublisherService service)
+ throws RemoteException {
+ VmsPublisherClientService vmsPublisherClientService = mVmsPublisherClientService.get();
+ if (vmsPublisherClientService == null) return;
+ if (DBG) {
+ Log.d(TAG, "setting VmsPublisherService.");
+ }
+ Handler handler = vmsPublisherClientService.mHandler;
+ handler.sendMessage(
+ handler.obtainMessage(VmsEventHandler.SET_SERVICE_CALLBACK, service));
+ vmsPublisherClientService.setToken(token);
+ }
+
+ @Override
+ public void onVmsSubscriptionChange(VmsSubscriptionState subscriptionState)
+ throws RemoteException {
+ VmsPublisherClientService vmsPublisherClientService = mVmsPublisherClientService.get();
+ if (vmsPublisherClientService == null) return;
+ if (DBG) {
+ Log.d(TAG, "subscription event: " + subscriptionState);
+ }
+ synchronized (mSequenceLock) {
+ if (subscriptionState.getSequenceNumber() <= mSequence) {
+ Log.w(TAG, "Sequence out of order. Current sequence = " + mSequence
+ + "; expected new sequence = " + subscriptionState.getSequenceNumber());
+ // Do not propagate old notifications.
+ return;
+ } else {
+ mSequence = subscriptionState.getSequenceNumber();
+ }
+ }
+ Handler handler = vmsPublisherClientService.mHandler;
+ handler.sendMessage(
+ handler.obtainMessage(VmsEventHandler.ON_SUBSCRIPTION_CHANGE_EVENT,
+ subscriptionState));
+ }
+ }
+
+ /**
+ * Receives events from the binder thread and dispatches them.
+ */
+ private final static class VmsEventHandler extends Handler {
+ /** Constants handled in the handler */
+ private static final int ON_SUBSCRIPTION_CHANGE_EVENT = 0;
+ private static final int SET_SERVICE_CALLBACK = 1;
+
+ private final WeakReference<VmsPublisherClientService> mVmsPublisherClientService;
+
+ VmsEventHandler(VmsPublisherClientService service) {
+ super(Looper.getMainLooper());
+ mVmsPublisherClientService = new WeakReference<>(service);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ VmsPublisherClientService service = mVmsPublisherClientService.get();
+ if (service == null) return;
+ switch (msg.what) {
+ case ON_SUBSCRIPTION_CHANGE_EVENT:
+ VmsSubscriptionState subscriptionState = (VmsSubscriptionState) msg.obj;
+ service.onVmsSubscriptionChange(subscriptionState);
+ break;
+ case SET_SERVICE_CALLBACK:
+ service.setVmsPublisherService((IVmsPublisherService) msg.obj);
+ break;
+ default:
+ Log.e(TAG, "Event type not handled: " + msg.what);
+ break;
+ }
+ }
+ }
+}
diff --git a/car-lib/src/android/car/vms/VmsSubscriberManager.java b/car-lib/src/android/car/vms/VmsSubscriberManager.java
new file mode 100644
index 0000000..97d4d86
--- /dev/null
+++ b/car-lib/src/android/car/vms/VmsSubscriberManager.java
@@ -0,0 +1,377 @@
+/*
+ * 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.annotation.SystemApi;
+import android.car.Car;
+import android.car.CarManagerBase;
+import android.car.CarNotConnectedException;
+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 com.android.internal.annotations.GuardedBy;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+
+/**
+ * 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
+ */
+@SystemApi
+public final class VmsSubscriberManager implements CarManagerBase {
+ private static final boolean DBG = true;
+ private static final String TAG = "VmsSubscriberManager";
+
+ private final Handler mHandler;
+ private final IVmsSubscriberService mVmsSubscriberService;
+ 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 VmsSubscriberClientCallback {
+ /**
+ * Called when the property is updated
+ */
+ void onVmsMessageReceived(VmsLayer layer, byte[] payload);
+
+ /**
+ * 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
+ */
+ private static final int ON_RECEIVE_MESSAGE_EVENT = 0;
+ private static final int ON_AVAILABILITY_CHANGE_EVENT = 1;
+
+ private final WeakReference<VmsSubscriberManager> mMgr;
+
+ VmsEventHandler(VmsSubscriberManager mgr, Looper looper) {
+ super(looper);
+ mMgr = new WeakReference<>(mgr);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ VmsSubscriberManager mgr = mMgr.get();
+ switch (msg.what) {
+ case ON_RECEIVE_MESSAGE_EVENT:
+ if (mgr != null) {
+ // Parse the message
+ VmsDataMessage vmsDataMessage = (VmsDataMessage) msg.obj;
+
+ // Dispatch the parsed message
+ mgr.dispatchOnReceiveMessage(vmsDataMessage.getLayer(),
+ vmsDataMessage.getPayload());
+ }
+ break;
+ case ON_AVAILABILITY_CHANGE_EVENT:
+ if (mgr != null) {
+ // Parse the message
+ List<VmsLayer> vmsAvailabilityChangeMessage = (List<VmsLayer>) msg.obj;
+
+ // Dispatch the parsed message
+ mgr.dispatchOnAvailabilityChangeMessage(vmsAvailabilityChangeMessage);
+ }
+ break;
+
+ default:
+ Log.e(VmsSubscriberManager.TAG, "Event type not handled: " + msg.what);
+ break;
+ }
+ }
+ }
+
+ public VmsSubscriberManager(IBinder service, Handler handler) {
+ mVmsSubscriberService = IVmsSubscriberService.Stub.asInterface(service);
+ mHandler = new VmsEventHandler(this, handler.getLooper());
+ mSubscriberManagerClient = new IVmsSubscriberClient.Stub() {
+ @Override
+ public void onVmsMessageReceived(VmsLayer layer, byte[] payload)
+ throws RemoteException {
+ // Create the data message
+ VmsDataMessage vmsDataMessage = new VmsDataMessage(layer, payload);
+ mHandler.sendMessage(
+ mHandler.obtainMessage(
+ VmsEventHandler.ON_RECEIVE_MESSAGE_EVENT,
+ vmsDataMessage));
+ }
+
+ @Override
+ public void onLayersAvailabilityChanged(List<VmsAssociatedLayer> availableLayers) {
+ mHandler.sendMessage(
+ mHandler.obtainMessage(
+ VmsEventHandler.ON_AVAILABILITY_CHANGE_EVENT,
+ availableLayers));
+ }
+ };
+ }
+
+ /**
+ * 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>
+ *
+ * @param clientCallback subscriber callback that will handle onVmsMessageReceived events.
+ * @throws IllegalStateException if the client callback was already set.
+ */
+ public void registerClientCallback(VmsSubscriberClientCallback clientCallback)
+ throws CarNotConnectedException {
+ synchronized (mClientCallbackLock) {
+ if (mClientCallback != null) {
+ throw new IllegalStateException("Client callback is already configured.");
+ }
+ 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 {
+ try {
+ return mVmsSubscriberService.getPublisherInfo(publisherId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not connect: ", e);
+ throw new CarNotConnectedException(e);
+ } catch (IllegalStateException ex) {
+ Car.checkCarNotConnectedExceptionFromCarService(ex);
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ /**
+ * Subscribes to listen to the layer specified.
+ *
+ * @param layer the layer to subscribe to.
+ * @throws IllegalStateException if the client callback was not set via
+ * {@link #registerClientCallback}.
+ */
+ public void subscribe(VmsLayer layer) throws CarNotConnectedException {
+ verifySubscriptionIsAllowed();
+ try {
+ mVmsSubscriberService.addVmsSubscriber(mSubscriberManagerClient, layer);
+ VmsOperationRecorder.get().subscribe(layer);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not connect: ", e);
+ throw new CarNotConnectedException(e);
+ } catch (IllegalStateException ex) {
+ Car.checkCarNotConnectedExceptionFromCarService(ex);
+ }
+ }
+
+ /**
+ * 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.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);
+ } catch (IllegalStateException ex) {
+ Car.checkCarNotConnectedExceptionFromCarService(ex);
+ }
+ }
+
+ /**
+ * Unsubscribes from the layer/version specified.
+ *
+ * @param layer the layer to unsubscribe from.
+ * @throws IllegalStateException if the client callback was not set via
+ * {@link #registerClientCallback}.
+ */
+ public void unsubscribe(VmsLayer layer) {
+ verifySubscriptionIsAllowed();
+ try {
+ mVmsSubscriberService.removeVmsSubscriber(mSubscriberManagerClient, layer);
+ VmsOperationRecorder.get().unsubscribe(layer);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to unregister subscriber", e);
+ // ignore
+ } catch (IllegalStateException ex) {
+ Car.hideCarNotConnectedExceptionFromCarService(ex);
+ }
+ }
+
+ /**
+ * 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.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
+ } catch (IllegalStateException ex) {
+ Car.hideCarNotConnectedExceptionFromCarService(ex);
+ }
+ }
+
+ private void dispatchOnReceiveMessage(VmsLayer layer, byte[] payload) {
+ VmsSubscriberClientCallback clientCallback = getClientCallbackThreadSafe();
+ if (clientCallback == null) {
+ Log.e(TAG, "Cannot dispatch received message.");
+ return;
+ }
+ clientCallback.onVmsMessageReceived(layer, payload);
+ }
+
+ private void dispatchOnAvailabilityChangeMessage(List<VmsLayer> availableLayers) {
+ VmsSubscriberClientCallback clientCallback = getClientCallbackThreadSafe();
+ if (clientCallback == null) {
+ Log.e(TAG, "Cannot dispatch availability change message.");
+ return;
+ }
+ clientCallback.onLayersAvailabilityChanged(availableLayers);
+ }
+
+ 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() {
+ }
+
+ private static final class VmsDataMessage {
+ private final VmsLayer mLayer;
+ private final byte[] mPayload;
+
+ public VmsDataMessage(VmsLayer layer, byte[] payload) {
+ mLayer = layer;
+ mPayload = payload;
+ }
+
+ public VmsLayer getLayer() {
+ return mLayer;
+ }
+
+ public byte[] getPayload() {
+ return mPayload;
+ }
+ }
+}
diff --git a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl b/car-lib/src/android/car/vms/VmsSubscriptionState.aidl
similarity index 67%
rename from libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
rename to car-lib/src/android/car/vms/VmsSubscriptionState.aidl
index ca0ac83..b5ce8ff 100644
--- a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
+++ b/car-lib/src/android/car/vms/VmsSubscriptionState.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,12 +14,6 @@
* limitations under the License.
*/
-package com.android.car.vehiclemonitor;
+package android.car.vms;
-/**
- * Listener for vehicle monitor service.
- * @hide
- */
-oneway interface IVehicleMonitorListener {
- void onAppViolation(int pid, int uid, int action, int violation) = 0;
-}
+parcelable VmsSubscriptionState;
\ No newline at end of file
diff --git a/car-lib/src/android/car/vms/VmsSubscriptionState.java b/car-lib/src/android/car/vms/VmsSubscriptionState.java
new file mode 100644
index 0000000..1aba30c
--- /dev/null
+++ b/car-lib/src/android/car/vms/VmsSubscriptionState.java
@@ -0,0 +1,118 @@
+/*
+ * 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.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.
+ *
+ * @hide
+ */
+public final class VmsSubscriptionState implements Parcelable {
+ private final int mSequenceNumber;
+ private final Set<VmsLayer> mLayers;
+ private final Set<VmsAssociatedLayer> mSubscribedLayersFromPublishers;
+
+ /**
+ * 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,
+ Set<VmsLayer> subscribedLayers,
+ Set<VmsAssociatedLayer> layersFromPublishers) {
+ mSequenceNumber = sequenceNumber;
+ 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 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) {
+ sb.append(layer).append(",");
+ }
+ sb.append("}");
+ sb.append("; associatedLayers={");
+ for (VmsAssociatedLayer layer : mSubscribedLayersFromPublishers) {
+ sb.append(layer).append(",");
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ 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];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mSequenceNumber);
+ out.writeParcelableList(new ArrayList(mLayers), flags);
+ out.writeParcelableList(new ArrayList(mSubscribedLayersFromPublishers), flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private VmsSubscriptionState(Parcel in) {
+ mSequenceNumber = in.readInt();
+
+ List<VmsLayer> layers = new ArrayList<>();
+ in.readParcelableList(layers, VmsLayer.class.getClassLoader());
+ 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-lib/src_feature_current/com/android/car/internal/FeatureConfiguration.java b/car-lib/src_feature_current/com/android/car/internal/FeatureConfiguration.java
index 55da870..462f320 100644
--- a/car-lib/src_feature_current/com/android/car/internal/FeatureConfiguration.java
+++ b/car-lib/src_feature_current/com/android/car/internal/FeatureConfiguration.java
@@ -25,5 +25,4 @@
public static final boolean DEFAULT = false;
/** product configuration in CarInfoManager */
public static final boolean ENABLE_PRODUCT_CONFIGURATION_INFO = DEFAULT;
- public static final boolean ENABLE_VEHICLE_MAP_SERVICE = DEFAULT;
}
diff --git a/car-maps-placeholder/AndroidManifest.xml b/car-maps-placeholder/AndroidManifest.xml
index e166420..38caf38 100644
--- a/car-maps-placeholder/AndroidManifest.xml
+++ b/car-maps-placeholder/AndroidManifest.xml
@@ -31,8 +31,6 @@
android:resizeableActivity="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- <category android:name="android.intent.category.APP_MAPS" />
</intent-filter>
</activity>
</application>
diff --git a/car-support-lib/AndroidManifest.xml b/car-support-lib/AndroidManifest.xml
index 36b1649..f92f21b 100644
--- a/car-support-lib/AndroidManifest.xml
+++ b/car-support-lib/AndroidManifest.xml
@@ -17,6 +17,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
package="android.support.car.ui" >
- <uses-sdk android:minSdkVersion="23"
+ <uses-sdk android:minSdkVersion="21"
android:targetSdkVersion="23" />
</manifest>
diff --git a/car-support-lib/proguard-extra-keeps.flags b/car-support-lib/proguard-extra-keeps.flags
index 5e67c6e..72ea927 100644
--- a/car-support-lib/proguard-extra-keeps.flags
+++ b/car-support-lib/proguard-extra-keeps.flags
@@ -17,3 +17,7 @@
public static ... createCluster(...);
public static ... createCustomImageCluster(...);
}
+
+-keep class android.support.car.CarManagerBase {
+ *;
+}
diff --git a/car-support-lib/src/android/support/car/Car.java b/car-support-lib/src/android/support/car/Car.java
index a374d87..e0e3a0d 100644
--- a/car-support-lib/src/android/support/car/Car.java
+++ b/car-support-lib/src/android/support/car/Car.java
@@ -237,6 +237,7 @@
if (mConnectionState == STATE_DISCONNECTED) {
return;
}
+ tearDownCarManagers();
mConnectionState = STATE_DISCONNECTED;
}
mCarConnectionCallback.onDisconnected(Car.this);
diff --git a/car_product/build/car.mk b/car_product/build/car.mk
index 9984726..5470324 100644
--- a/car_product/build/car.mk
+++ b/car_product/build/car.mk
@@ -31,6 +31,7 @@
# This is for testing
PRODUCT_PACKAGES += \
+ DefaultStorageMonitoringCompanionApp \
EmbeddedKitchenSinkApp \
VmsPublisherClientSample \
VmsSubscriberClientSample \
@@ -74,7 +75,6 @@
# Automotive specific packages
PRODUCT_PACKAGES += \
- vehicle_monitor_service \
CarService \
CarTrustAgentService \
CarDialerApp \
@@ -89,7 +89,7 @@
CarLatinIME \
CarUsbHandler \
android.car \
- libvehiclemonitor-native \
+ com.android.car.procfsinspector \
# Boot animation
PRODUCT_COPY_FILES += \
diff --git a/car_product/build/car_base.mk b/car_product/build/car_base.mk
index 815b9ba..9138090 100644
--- a/car_product/build/car_base.mk
+++ b/car_product/build/car_base.mk
@@ -84,11 +84,10 @@
# EVS resources
PRODUCT_PACKAGES += android.automotive.evs.manager@1.0
-PRODUCT_PACKAGES += evs_app
# The following packages, or their vendor specific equivalents should be include in the device.mk
+#PRODUCT_PACKAGES += evs_app
#PRODUCT_PACKAGES += evs_app_default_resources
#PRODUCT_PACKAGES += android.hardware.automotive.evs@1.0-service
-#PRODUCT_PACKAGES += android.hardware.automotive.evs@1.0-sample
# Device running Android is a car
PRODUCT_COPY_FILES += \
diff --git a/car_product/init/init.car.rc b/car_product/init/init.car.rc
index 8cec36e..1ca29ce 100644
--- a/car_product/init/init.car.rc
+++ b/car_product/init/init.car.rc
@@ -1,8 +1 @@
-service vms /system/bin/vehicle_monitor_service
- class core
- user root
- group root
- critical
-
-on boot
- start vms
+# Insert car-specific startup services here
diff --git a/car_product/overlay/frameworks/base/core/res/res/values-sw900dp/dimens.xml b/car_product/overlay/frameworks/base/core/res/res/values-sw900dp/dimens.xml
new file mode 100644
index 0000000..d1af884
--- /dev/null
+++ b/car_product/overlay/frameworks/base/core/res/res/values-sw900dp/dimens.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.
+*/
+-->
+<resources>
+
+ <!-- Height of the bottom navigation / system bar. -->
+ <dimen name="navigation_bar_height">112dp</dimen>
+
+ <!-- Height of the bottom navigation bar in landscape; often
+ the same as @dimen/navigation_bar_height -->
+ <dimen name="navigation_bar_height_landscape">112dp</dimen>
+
+</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 ca8a8d8..5778970 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
@@ -69,4 +69,12 @@
<!-- Home screen(Launcher) app presence -->
<bool name="config_noHomeScreen">true</bool>
+
+ <!-- Flag indicating that this device does not rotate and will always remain in its default
+ orientation. Activities that desire to run in a non-compatible orientation will find that
+ they are not able to. -->
+ <bool name="config_forceDefaultOrientation">true</bool>
+
+ <!-- Corner radius of system dialogs -->
+ <dimen name="config_dialogCornerRadius">16dp</dimen>
</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 94a6d45..030b422 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
@@ -57,6 +57,6 @@
<item>com.android.car.dialer</item>
<item>com.android.car.overview</item>
<item></item>
- <item>com.android.car.hvac;com.android.settings;com.android.car.settings;com.android.vending;com.google.android.car.bugreport;com.google.android.car.kitchensink;com.android.car.systemupdater;org.chromium.webview_shell;com.android.contacts;org.codeaurora.bluetooth.bttestapp;com.google.android.projection.sink</item>
+ <item>com.android.car.hvac;com.android.settings;com.android.car.settings;com.android.vending;com.google.android.car.bugreport;com.google.android.car.kitchensink;com.android.car.systemupdater;org.chromium.webview_shell;com.android.contacts;org.codeaurora.bluetooth.bttestapp;com.google.android.projection.sink;com.google.android.googlequicksearchbox</item>
</array>
</resources>
diff --git a/car_product/sepolicy/vehicle_monitor_service.te b/car_product/sepolicy/vehicle_monitor_service.te
deleted file mode 100644
index 16c6f81..0000000
--- a/car_product/sepolicy/vehicle_monitor_service.te
+++ /dev/null
@@ -1,4 +0,0 @@
-type vehicle_monitor_service_exec, exec_type, file_type;
-type vehicle_monitor_service, domain;
-
-init_daemon_domain(vehicle_monitor_service)
diff --git a/evs/app/RenderTopView.cpp b/evs/app/RenderTopView.cpp
index 1579a0a..bff2b3c 100644
--- a/evs/app/RenderTopView.cpp
+++ b/evs/app/RenderTopView.cpp
@@ -133,7 +133,7 @@
// Load the checkerboard text image
mTexAssets.checkerBoard.reset(createTextureFromPng(
"/system/etc/automotive/evs/LabeledChecker.png"));
- if (!mTexAssets.checkerBoard->glId()) {
+ if (!mTexAssets.checkerBoard) {
ALOGE("Failed to load checkerboard texture");
return false;
}
@@ -141,7 +141,7 @@
// Load the car image
mTexAssets.carTopView.reset(createTextureFromPng(
"/system/etc/automotive/evs/CarFromTop.png"));
- if (!mTexAssets.carTopView->glId()) {
+ if (!mTexAssets.carTopView) {
ALOGE("Failed to load carTopView texture");
return false;
}
diff --git a/evs/app/WindowSurface.cpp b/evs/app/WindowSurface.cpp
index a3f56bc..04e7ac4 100644
--- a/evs/app/WindowSurface.cpp
+++ b/evs/app/WindowSurface.cpp
@@ -62,19 +62,10 @@
return;
}
- SurfaceComposerClient::openGlobalTransaction();
- err = sc->setLayer(0x7FFFFFFF); // always on top
- if (err != NO_ERROR) {
- fprintf(stderr, "SurfaceComposer::setLayer error: %#x\n", err);
- return;
- }
-
- err = sc->show();
- if (err != NO_ERROR) {
- fprintf(stderr, "SurfaceComposer::show error: %#x\n", err);
- return;
- }
- SurfaceComposerClient::closeGlobalTransaction();
+ SurfaceComposerClient::Transaction{}
+ .setLayer(sc, 0x7FFFFFFF) // always on top
+ .show(sc)
+ .apply();
mSurfaceControl = sc;
}
diff --git a/evs/app/evs_app.cpp b/evs/app/evs_app.cpp
index 618e8cc..521bc47 100644
--- a/evs/app/evs_app.cpp
+++ b/evs/app/evs_app.cpp
@@ -97,7 +97,10 @@
// Load our configuration information
ConfigManager config;
- config.initialize("/system/etc/automotive/evs/config.json");
+ if (!config.initialize("/system/etc/automotive/evs/config.json")) {
+ ALOGE("Missing or improper configuration for the EVS application. Exiting.");
+ return 1;
+ }
// Set thread pool size to one to avoid concurrent events from the HAL.
// This pool will handle the EvsCameraStream callbacks.
diff --git a/evs/sampleDriver/GlWrapper.cpp b/evs/sampleDriver/GlWrapper.cpp
index eb45bc9..3055ba0 100644
--- a/evs/sampleDriver/GlWrapper.cpp
+++ b/evs/sampleDriver/GlWrapper.cpp
@@ -339,19 +339,19 @@
void GlWrapper::showWindow() {
if (mFlingerSurfaceControl != nullptr) {
- SurfaceComposerClient::openGlobalTransaction();
- mFlingerSurfaceControl->setLayer(0x7FFFFFFF); // always on top
- mFlingerSurfaceControl->show();
- SurfaceComposerClient::closeGlobalTransaction();
+ SurfaceComposerClient::Transaction{}
+ .setLayer(mFlingerSurfaceControl, 0x7FFFFFFF) // always on top
+ .show(mFlingerSurfaceControl)
+ .apply();
}
}
void GlWrapper::hideWindow() {
if (mFlingerSurfaceControl != nullptr) {
- SurfaceComposerClient::openGlobalTransaction();
- mFlingerSurfaceControl->hide();
- SurfaceComposerClient::closeGlobalTransaction();
+ SurfaceComposerClient::Transaction{}
+ .hide(mFlingerSurfaceControl)
+ .apply();
}
}
diff --git a/libvehiclemonitor/include/HandlerThread.h b/libvehiclemonitor/include/HandlerThread.h
deleted file mode 100644
index 03f523e..0000000
--- a/libvehiclemonitor/include/HandlerThread.h
+++ /dev/null
@@ -1,49 +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.
- */
-
-#ifndef LIBVEHICLEMONITOR_HANDLER_THREAD_H_
-#define LIBVEHICLEMONITOR_HANDLER_THREAD_H_
-
-#include <utils/Looper.h>
-#include <utils/threads.h>
-
-namespace android {
-
-/**
- * Native HandlerThread implementation looking similar to Java version.
- */
-class HandlerThread : public Thread {
-public:
- HandlerThread();
- virtual ~HandlerThread();
-
- sp<Looper> getLooper();
- status_t start(const char* name = 0, int32_t priority = PRIORITY_DEFAULT, size_t stack = 0);
- void quit();
-
-private:
- bool threadLoop();
-
-private:
- sp<Looper> mLooper;
- mutable Mutex mLock;
- bool mShouldQuit;
- Condition mLooperWait;
-};
-
-};
-
-#endif /* LIBVEHICLEMONITOR_HANDLER_THREAD_H_ */
diff --git a/libvehiclemonitor/include/IVehicleMonitor.h b/libvehiclemonitor/include/IVehicleMonitor.h
deleted file mode 100644
index 4aa2e14..0000000
--- a/libvehiclemonitor/include/IVehicleMonitor.h
+++ /dev/null
@@ -1,60 +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.
- */
-
-#ifndef ANDROID_IVEHICLE_MONITOR_H
-#define ANDROID_IVEHICLE_MONITOR_H
-
-#include <binder/Parcel.h>
-
-#include <IVehicleMonitorListener.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-/**
- * Application priorities used in vehicle monitoring.
- */
-enum vehicle_app_priority {
- VEHICLE_APP_PRIORITY_NONE = 0,
- VEHICLE_APP_PRIORITY_FOREGROUND = 1,
-};
-
-// ----------------------------------------------------------------------------
-
-class IVehicleMonitor : public IInterface {
-public:
- static const char SERVICE_NAME[];
- DECLARE_META_INTERFACE(VehicleMonitor);
-
- virtual status_t setAppPriority(
- uint32_t pid, uint32_t uid, vehicle_app_priority priority) = 0;
- virtual status_t setMonitorListener(
- const sp<IVehicleMonitorListener> &listener) = 0;
-};
-// ----------------------------------------------------------------------------
-
-class BnVehicleMonitor : public BnInterface<IVehicleMonitor> {
- virtual status_t onTransact(uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif /* ANDROID_IVEHICLE_MONITOR_H */
diff --git a/libvehiclemonitor/include/IVehicleMonitorListener.h b/libvehiclemonitor/include/IVehicleMonitorListener.h
deleted file mode 100644
index 9c9e1f5..0000000
--- a/libvehiclemonitor/include/IVehicleMonitorListener.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_IVEHICLE_MONITOR_LISTENER_H
-#define ANDROID_IVEHICLE_MONITOR_LISTENER_H
-
-#include <binder/Parcel.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-class IVehicleMonitorListener : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(VehicleMonitorListener);
-
- /**
- * Notifies when app misbehaved. Client (Bn implementor) should
- * hold sp to keep the data received outside this call.
- */
- virtual void onAppViolation(
- int32_t pid, int32_t uid, int32_t action, int32_t violation) = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-class BnVehicleMonitorListener : public BnInterface<IVehicleMonitorListener>
-{
- virtual status_t onTransact(uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif /* ANDROID_IVEHICLE_MONITOR_LISTENER_H */
diff --git a/libvehiclemonitor/include/VehicleMonitor.h b/libvehiclemonitor/include/VehicleMonitor.h
deleted file mode 100644
index 12510ec..0000000
--- a/libvehiclemonitor/include/VehicleMonitor.h
+++ /dev/null
@@ -1,66 +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.
- */
-
-#ifndef ANDROID_VEHICLE_MONITOR_H
-#define ANDROID_VEHICLE_MONITOR_H
-
-#include <binder/IInterface.h>
-
-#include <utils/Mutex.h>
-
-#include "IVehicleMonitor.h"
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-/**
- * Vehicle monitor API for low level components like HALs to access
- * monitoring service.
- * This is reference counted. So use with sp<>.
- */
-class VehicleMonitor : public IBinder::DeathRecipient {
-public:
- /**
- * Factory method for VehicleMonitor. Client should use this method
- * to create a new instance.
- */
- static sp<VehicleMonitor> createVehicleMonitor();
-
- virtual ~VehicleMonitor();
-
- status_t setAppPriority(
- uint32_t pid, uint32_t uid, vehicle_app_priority priority);
-
- //IBinder::DeathRecipient, not for client
- void binderDied(const wp<IBinder>& who);
-
-private:
- VehicleMonitor(sp<IVehicleMonitor>& vehicleMonitor);
- // RefBase
- virtual void onFirstRef();
- sp<IVehicleMonitor> getService();
-
-private:
- sp<IVehicleMonitor> mService;
- Mutex mLock;
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif /* ANDROID_VEHICLE_MONITOR_H */
diff --git a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitor.aidl b/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitor.aidl
deleted file mode 100644
index 0c495ec..0000000
--- a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitor.aidl
+++ /dev/null
@@ -1,28 +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.android.car.vehiclemonitor;
-
-import com.android.car.vehiclemonitor.IVehicleMonitorListener;
-
-/**
- * Binder API to access vehicle monitor service.
- * @hide
- */
-oneway interface IVehicleMonitor {
- void setAppPriority(int pid, int uid, int priority) = 0;
- void setMonitorListener(IVehicleMonitorListener listener) = 1;
-}
diff --git a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/VehicleMonitor.java b/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/VehicleMonitor.java
deleted file mode 100644
index f5a9363..0000000
--- a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/VehicleMonitor.java
+++ /dev/null
@@ -1,182 +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.android.car.vehiclemonitor;
-
-import android.annotation.IntDef;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.ServiceSpecificException;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
-
-/**
- * System API to access Vehicle monitor. This is only for system services and applications should
- * not use this. All APIs will fail with security error if normal app tries this.
- */
-public class VehicleMonitor {
- private static final String TAG = VehicleMonitor.class.getSimpleName();
-
- private final IVehicleMonitor mService;
- private final VehicleMonitorListener mListener;
- private final IVehicleMonitorListenerImpl mVehicleMonitorListener;
- private final EventHandler mEventHandler;
-
- private static final int VMS_CONNECT_MAX_RETRY = 10;
- private static final long VMS_RETRY_WAIT_TIME_MS = 1000;
-
- /**
- * Application priorities used in vehicle monitoring.
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({
- ApplicationPriority.NONE,
- ApplicationPriority.FOREGROUND
- })
- public @interface ApplicationPriority {
- int NONE = 0;
- int FOREGROUND = 1;
- }
-
- /**
- * Listener for VMS events.
- */
- public interface VehicleMonitorListener {
- void onAppViolation(int pid, int uid, int action, int violation);
- }
-
- /**
- * Factory method to create VehicleMonitor
- */
- public static VehicleMonitor createVehicleMonitor(
- VehicleMonitorListener listener, Looper looper) {
- int retryCount = 0;
- IVehicleMonitor service = null;
- while (true) {
- service = IVehicleMonitor.Stub.asInterface(
- ServiceManager.getService(IVehicleMonitor.class.getCanonicalName()));
- if (service != null) {
- break;
- }
- retryCount++;
- if (retryCount > VMS_CONNECT_MAX_RETRY) {
- break;
- }
- try {
- Thread.sleep(VMS_RETRY_WAIT_TIME_MS);
- } catch (InterruptedException e) {
- //ignore
- }
- }
- if (service == null) {
- throw new RuntimeException("Vehicle monitor service not available:"
- + IVehicleMonitor.class.getCanonicalName());
- }
- return new VehicleMonitor(service, listener, looper);
- }
-
- private VehicleMonitor(
- IVehicleMonitor service, VehicleMonitorListener listener, Looper looper) {
- mService = service;
- mListener = listener;
- mEventHandler = new EventHandler(looper);
- mVehicleMonitorListener = new IVehicleMonitorListenerImpl(this);
- try {
- mService.setMonitorListener(mVehicleMonitorListener);
- } catch (RemoteException e) {
- throw new RuntimeException("Vehicle monitor service not working ", e);
- }
- }
-
- /**
- * Set application priority.
- * <p>
- * This will lead into writing application priority into vehicle monitor.
- */
- public void setAppPriority(int pid, int uid, @ApplicationPriority int priority)
- throws ServiceSpecificException {
- try {
- mService.setAppPriority(pid, uid, priority);
- } catch (RemoteException e) {
- throw new RuntimeException("Vehicle monitor service not working ", e);
- }
- }
-
- private void handleVehicleMonitorAppViolation(AppViolation appViolation) {
- mListener.onAppViolation(appViolation.mPid, appViolation.mUid, appViolation.mAction,
- appViolation.mViolation);
- }
-
- private class EventHandler extends Handler {
-
- private static final int MSG_APP_VIOLATION = 0;
-
- private EventHandler(Looper looper) {
- super(looper);
- }
-
- private void notifyAppViolation(int pid, int uid, int action, int violation) {
- AppViolation appViolation = new AppViolation(pid, uid, action, violation);
- Message msg = obtainMessage(MSG_APP_VIOLATION, appViolation);
- sendMessage(msg);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_APP_VIOLATION:
- AppViolation appViolation = (AppViolation) msg.obj;
- handleVehicleMonitorAppViolation(appViolation);
- break;
- }
- }
- }
-
- private static class IVehicleMonitorListenerImpl extends IVehicleMonitorListener.Stub {
-
- private final WeakReference<VehicleMonitor> mVehicleMonitor;
-
- private IVehicleMonitorListenerImpl(VehicleMonitor vehicleNewotk) {
- mVehicleMonitor = new WeakReference<>(vehicleNewotk);
- }
-
- @Override
- public void onAppViolation(int pid, int uid, int action, int violation) {
- VehicleMonitor vehicleMonitor = mVehicleMonitor.get();
- if (vehicleMonitor != null) {
- vehicleMonitor.mEventHandler.notifyAppViolation(pid, uid, action, violation);
- }
- }
- }
-
- private static class AppViolation {
- public final int mPid;
- public final int mUid;
- public final int mAction;
- public final int mViolation;
-
- AppViolation(int pid, int uid, int action, int violation) {
- mPid = pid;
- mUid = uid;
- mAction = action;
- mViolation = violation;
- }
- }
-}
diff --git a/libvehiclemonitor/native/HandlerThread.cpp b/libvehiclemonitor/native/HandlerThread.cpp
deleted file mode 100644
index 5ee2715..0000000
--- a/libvehiclemonitor/native/HandlerThread.cpp
+++ /dev/null
@@ -1,72 +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.
- */
-
-#include "HandlerThread.h"
-
-namespace android {
-
-HandlerThread::HandlerThread()
- : mShouldQuit(false) {
-
-}
-
-HandlerThread::~HandlerThread() {
- quit();
-}
-
-sp<Looper> HandlerThread::getLooper() {
- Mutex::Autolock autoLock(mLock);
- if (mLooper.get() == 0) {
- mLooperWait.wait(mLock);
- }
- return mLooper;
-}
-
-status_t HandlerThread::start(const char* name, int32_t priority, size_t stack) {
- return run(name, priority, stack);
-}
-
-void HandlerThread::quit() {
- if (!isRunning()) {
- return;
- }
- sp<Looper> looper = getLooper();
- mLock.lock();
- mShouldQuit = true;
- mLock.unlock();
- looper->wake();
- requestExitAndWait();
-}
-
-bool HandlerThread::threadLoop() {
- mLock.lock();
- mLooper = Looper::prepare(0);
- mLooperWait.broadcast();
- mLock.unlock();
- while (true) {
- do {
- Mutex::Autolock autoLock(mLock);
- if (mShouldQuit) {
- return false;
- }
- } while (false);
- mLooper->pollOnce(-1);
- }
- return false;
-}
-
-
-};
diff --git a/libvehiclemonitor/native/IVehicleMonitor.cpp b/libvehiclemonitor/native/IVehicleMonitor.cpp
deleted file mode 100644
index b29f665..0000000
--- a/libvehiclemonitor/native/IVehicleMonitor.cpp
+++ /dev/null
@@ -1,128 +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.
- */
-
-#define LOG_TAG "VehicleMonitor"
-
-#include <binder/IPCThreadState.h>
-#include <binder/Status.h>
-#include <private/android_filesystem_config.h>
-
-#include <utils/Log.h>
-
-#include <IVehicleMonitor.h>
-
-namespace android {
-enum {
- SET_APP_PRIORITY = IBinder::FIRST_CALL_TRANSACTION,
- SET_MONITOR_LISTENER,
-};
-
-const char IVehicleMonitor::SERVICE_NAME[] = "com.android.car.vehiclemonitor.IVehicleMonitor";
-
-// ----------------------------------------------------------------------------
-
-class BpVehicleMonitor : public BpInterface<IVehicleMonitor> {
-public:
- explicit BpVehicleMonitor(const sp<IBinder> & impl)
- : BpInterface<IVehicleMonitor>(impl) {
- }
-
- virtual status_t setAppPriority(
- uint32_t pid, uint32_t uid, vehicle_app_priority priority) {
- Parcel data, reply;
- data.writeInterfaceToken(IVehicleMonitor::getInterfaceDescriptor());
- data.writeInt32(1);
- data.writeInt32(pid);
- data.writeInt32(uid);
- data.writeInt32(priority);
- status_t status = remote()->transact(SET_APP_PRIORITY, data, &reply);
- if (status == NO_ERROR) {
- int32_t exceptionCode = reply.readExceptionCode();
- if (exceptionCode != NO_ERROR) {
- if (exceptionCode == binder::Status::EX_SERVICE_SPECIFIC) {
- return -EAGAIN;
- } else if (exceptionCode == binder::Status::EX_ILLEGAL_STATE) {
- return -ESHUTDOWN;
- }
- return exceptionCode;
- }
- }
- return status;
- }
-
- virtual status_t setMonitorListener(
- const sp<IVehicleMonitorListener> &listener) {
- Parcel data, reply;
- data.writeInterfaceToken(IVehicleMonitor::getInterfaceDescriptor());
- data.writeStrongBinder(IInterface::asBinder(listener));
- status_t status = remote()->transact(SET_MONITOR_LISTENER, data, &reply);
- return status;
- }
-};
-
-IMPLEMENT_META_INTERFACE(VehicleMonitor, IVehicleMonitor::SERVICE_NAME);
-
-// ----------------------------------------------------------------------
-
-static bool isSystemUser() {
- uid_t uid = IPCThreadState::self()->getCallingUid();
- switch (uid) {
- // This list will be expanded. Only these UIDs are allowed to access vehicle monitor.
- case AID_ROOT:
- case AID_SYSTEM: {
- return true;
- } break;
- default: {
- ALOGE("non-system user tried access, uid %d", uid);
- } break;
- }
- return false;
-}
-
-status_t BnVehicleMonitor::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags) {
- if (!isSystemUser()) {
- return PERMISSION_DENIED;
- }
- status_t r;
- switch (code) {
- case SET_APP_PRIORITY: {
- CHECK_INTERFACE(IVehicleMonitor, data, reply);
- if (data.readInt32() == 0) { // no data
- ALOGE("null data");
- return BAD_VALUE;
- }
- int32_t pid = data.readInt32();
- int32_t uid = data.readInt32();
- int32_t priority = data.readInt32();
- r = setAppPriority(pid, uid, (vehicle_app_priority) priority);
- reply->writeNoException();
- return r;
- } break;
- case SET_MONITOR_LISTENER: {
- CHECK_INTERFACE(IVehicleMonitor, data, reply);
- sp<IVehicleMonitorListener> listener =
- interface_cast<IVehicleMonitorListener>(data.readStrongBinder());
- r = setMonitorListener(listener);
- reply->writeNoException();
- return r;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-}; // namespace android
diff --git a/libvehiclemonitor/native/IVehicleMonitorListener.cpp b/libvehiclemonitor/native/IVehicleMonitorListener.cpp
deleted file mode 100644
index 985a228..0000000
--- a/libvehiclemonitor/native/IVehicleMonitorListener.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.
- */
-
-
-
-#define LOG_TAG "VehicleMonitorListener"
-
-#include <memory>
-
-#include <utils/Errors.h>
-#include <utils/Log.h>
-
-#include <IVehicleMonitorListener.h>
-
-namespace android {
-
-enum {
- ON_APP_VIOLATION = IBinder::FIRST_CALL_TRANSACTION,
-};
-
-class BpVehicleMonitorListener : public BpInterface<IVehicleMonitorListener>
-{
- public:
- explicit BpVehicleMonitorListener(const sp<IBinder> & impl)
- : BpInterface<IVehicleMonitorListener>(impl) {
- }
-
- virtual void onAppViolation(
- int32_t pid, int32_t uid, int32_t action, int32_t violation) {
- Parcel data, reply;
- data.writeInterfaceToken(IVehicleMonitorListener::getInterfaceDescriptor());
- data.writeInt32(1);
- data.writeInt32(pid);
- data.writeInt32(uid);
- data.writeInt32(action);
- data.writeInt32(violation);
- remote()->transact(ON_APP_VIOLATION, data, &reply, IBinder::FLAG_ONEWAY);
- }
-};
-
-IMPLEMENT_META_INTERFACE(VehicleMonitorListener, "com.android.car.vehiclemonitor.IVehicleMonitorListener");
-
-// ----------------------------------------------------------------------
-
-status_t BnVehicleMonitorListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags) {
- status_t r;
- switch (code) {
- case ON_APP_VIOLATION: {
- CHECK_INTERFACE(IVehicleMonitorListener, data, reply);
- if (data.readInt32() == 0) { // java side allows passing null with this.
- return BAD_VALUE;
- }
- int32_t pid = data.readInt32();
- int32_t uid = data.readInt32();
- int32_t action = data.readInt32();
- int32_t violation = data.readInt32();
- onAppViolation(pid, uid, action, violation);
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-}; // namespace android
diff --git a/libvehiclemonitor/native/VehicleMonitor.cpp b/libvehiclemonitor/native/VehicleMonitor.cpp
deleted file mode 100644
index 33235cc..0000000
--- a/libvehiclemonitor/native/VehicleMonitor.cpp
+++ /dev/null
@@ -1,90 +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.
- */
-#define LOG_TAG "VehicleMonitor.Lib"
-
-#include <assert.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
-
-#include <VehicleMonitor.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-static const int MAX_SERVICE_RETRY = 4;
-
-sp<VehicleMonitor> VehicleMonitor::createVehicleMonitor() {
- sp<IBinder> binder;
- int retry = 0;
- while (true) {
- binder = defaultServiceManager()->getService(String16(IVehicleMonitor::SERVICE_NAME));
- if (binder.get() != NULL) {
- break;
- }
- retry++;
- if (retry > MAX_SERVICE_RETRY) {
- ALOGE("cannot get VMS, will crash");
- break;
- }
- }
- assert(binder.get() != NULL);
- sp<IVehicleMonitor> ivm(interface_cast<IVehicleMonitor>(binder));
- sp<VehicleMonitor> vm;
- vm = new VehicleMonitor(ivm);
- assert(vm.get() != NULL);
- // in case thread pool is not started, start it.
- ProcessState::self()->startThreadPool();
- return vm;
-}
-
-VehicleMonitor::VehicleMonitor(sp<IVehicleMonitor>& vehicleMonitor) :
- mService(vehicleMonitor) {
-}
-
-VehicleMonitor::~VehicleMonitor() {
- sp<IVehicleMonitor> service = getService();
- IInterface::asBinder(service)->unlinkToDeath(this);
-}
-
-void VehicleMonitor::onFirstRef() {
- sp<IVehicleMonitor> service = getService();
- IInterface::asBinder(service)->linkToDeath(this);
-}
-
-status_t VehicleMonitor::setAppPriority(
- uint32_t pid, uint32_t uid, vehicle_app_priority priority) {
- return getService()->setAppPriority(pid, uid, priority);
-}
-
-void VehicleMonitor::binderDied(const wp<IBinder>& who) {
- ALOGE("service died");
- {
- Mutex::Autolock autoLock(mLock);
- sp<IBinder> ibinder = who.promote();
- ibinder->unlinkToDeath(this);
- sp<IBinder> binder = defaultServiceManager()->getService(
- String16(IVehicleMonitor::SERVICE_NAME));
- mService = interface_cast<IVehicleMonitor>(binder);
- IInterface::asBinder(mService)->linkToDeath(this);
- };
-}
-
-sp<IVehicleMonitor> VehicleMonitor::getService() {
- Mutex::Autolock autoLock(mLock);
- return mService;
-}
-
-}; // namespace android
diff --git a/libvehiclemonitor/Android.mk b/procfs-inspector/Android.mk
similarity index 92%
rename from libvehiclemonitor/Android.mk
rename to procfs-inspector/Android.mk
index 23bc789..89bdf30 100644
--- a/libvehiclemonitor/Android.mk
+++ b/procfs-inspector/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2016 The Android Open Source Project
+# 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.
diff --git a/libvehiclemonitor/java/Android.mk b/procfs-inspector/client/Android.mk
similarity index 87%
rename from libvehiclemonitor/java/Android.mk
rename to procfs-inspector/client/Android.mk
index 1ced365..d9713de 100644
--- a/libvehiclemonitor/java/Android.mk
+++ b/procfs-inspector/client/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2016 The Android Open Source Project
+# 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.
@@ -18,7 +18,7 @@
include $(CLEAR_VARS)
-LOCAL_MODULE := libvehiclemonitor-java
+LOCAL_MODULE := com.android.car.procfsinspector-client
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)
diff --git a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl b/procfs-inspector/client/src/com/android/car/procfsinspector/IProcfsInspector.aidl
similarity index 67%
copy from libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
copy to procfs-inspector/client/src/com/android/car/procfsinspector/IProcfsInspector.aidl
index ca0ac83..7022ff4 100644
--- a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
+++ b/procfs-inspector/client/src/com/android/car/procfsinspector/IProcfsInspector.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,12 +14,10 @@
* limitations under the License.
*/
-package com.android.car.vehiclemonitor;
+package com.android.car.procfsinspector;
-/**
- * Listener for vehicle monitor service.
- * @hide
- */
-oneway interface IVehicleMonitorListener {
- void onAppViolation(int pid, int uid, int action, int violation) = 0;
+import com.android.car.procfsinspector.ProcessInfo;
+
+interface IProcfsInspector {
+ List<ProcessInfo> readProcessTable();
}
diff --git a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl b/procfs-inspector/client/src/com/android/car/procfsinspector/ProcessInfo.aidl
similarity index 67%
copy from libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
copy to procfs-inspector/client/src/com/android/car/procfsinspector/ProcessInfo.aidl
index ca0ac83..ca8d4be 100644
--- a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
+++ b/procfs-inspector/client/src/com/android/car/procfsinspector/ProcessInfo.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,12 +14,6 @@
* limitations under the License.
*/
-package com.android.car.vehiclemonitor;
+package com.android.car.procfsinspector;
-/**
- * Listener for vehicle monitor service.
- * @hide
- */
-oneway interface IVehicleMonitorListener {
- void onAppViolation(int pid, int uid, int action, int violation) = 0;
-}
+parcelable ProcessInfo;
diff --git a/procfs-inspector/client/src/com/android/car/procfsinspector/ProcessInfo.java b/procfs-inspector/client/src/com/android/car/procfsinspector/ProcessInfo.java
new file mode 100644
index 0000000..310e1ba
--- /dev/null
+++ b/procfs-inspector/client/src/com/android/car/procfsinspector/ProcessInfo.java
@@ -0,0 +1,77 @@
+/*
+ * 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.procfsinspector;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import java.util.Objects;
+
+public class ProcessInfo implements Parcelable {
+ public static final Parcelable.Creator<ProcessInfo> CREATOR =
+ new Parcelable.Creator<ProcessInfo>() {
+ public ProcessInfo createFromParcel(Parcel in) {
+ return new ProcessInfo(in);
+ }
+
+ public ProcessInfo[] newArray(int size) {
+ return new ProcessInfo[size];
+ }
+ };
+
+ public final int pid;
+ public final int uid;
+
+ public ProcessInfo(int pid, int uid) {
+ this.pid = pid;
+ this.uid = uid;
+ }
+
+ public ProcessInfo(Parcel in) {
+ this.pid = in.readInt();
+ this.uid = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(pid);
+ dest.writeInt(uid);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof ProcessInfo) {
+ ProcessInfo processInfo = (ProcessInfo)other;
+ return processInfo.pid == pid && processInfo.uid == uid;
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(pid, uid);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("pid = %d, uid = %d", pid, uid);
+ }
+}
diff --git a/procfs-inspector/client/src/com/android/car/procfsinspector/ProcfsInspector.java b/procfs-inspector/client/src/com/android/car/procfsinspector/ProcfsInspector.java
new file mode 100644
index 0000000..ff00bbf
--- /dev/null
+++ b/procfs-inspector/client/src/com/android/car/procfsinspector/ProcfsInspector.java
@@ -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 com.android.car.procfsinspector;
+
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+public final class ProcfsInspector {
+ private static final String TAG = "car.procfsinspector";
+ private static final String SERVICE_NAME = "com.android.car.procfsinspector";
+ private final IProcfsInspector mService;
+
+ private ProcfsInspector(IProcfsInspector service) {
+ mService = service;
+ }
+
+ @Nullable
+ private static IProcfsInspector tryGet() {
+ return IProcfsInspector.Stub.asInterface(
+ ServiceManager.getService(SERVICE_NAME));
+ }
+
+ public static List<ProcessInfo> readProcessTable() {
+ IProcfsInspector procfsInspector = tryGet();
+ if (procfsInspector != null) {
+ try {
+ return procfsInspector.readProcessTable();
+ } catch (RemoteException e) {
+ Log.w(TAG, "caught RemoteException", e);
+ }
+ }
+
+ return Collections.emptyList();
+ }
+}
diff --git a/libvehiclemonitor/native/Android.mk b/procfs-inspector/server/Android.mk
similarity index 61%
rename from libvehiclemonitor/native/Android.mk
rename to procfs-inspector/server/Android.mk
index 880fcae..d6a7d43 100644
--- a/libvehiclemonitor/native/Android.mk
+++ b/procfs-inspector/server/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2016 The Android Open Source Project
+# 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.
@@ -13,30 +13,36 @@
# limitations under the License.
#
#
+
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(patsubst ./%,%, $(shell cd $(LOCAL_PATH); \
- find . -name "*.cpp" -and -not -name ".*"))
+BOARD_SEPOLICY_DIRS += \
+ packages/services/Car/procfs-inspector/server/sepolicy
+
+LOCAL_SRC_FILES := \
+ main.cpp \
+ server.cpp \
+ impl.cpp \
+ process.cpp \
+ directory.cpp
LOCAL_C_INCLUDES += \
- frameworks/base/include \
- packages/services/Car/libvehiclemonitor/include
+ frameworks/base/include
LOCAL_SHARED_LIBRARIES := \
+ libbinder \
liblog \
- libutils \
- libhardware \
- libbinder
-
-LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libbinder
+ libutils
LOCAL_STRIP_MODULE := keep_symbols
-LOCAL_MODULE := libvehiclemonitor-native
+LOCAL_INIT_RC := com.android.car.procfsinspector.rc
+
+LOCAL_MODULE := com.android.car.procfsinspector
LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Wall -Werror
-include $(BUILD_SHARED_LIBRARY)
+include $(BUILD_EXECUTABLE)
diff --git a/procfs-inspector/server/com.android.car.procfsinspector.rc b/procfs-inspector/server/com.android.car.procfsinspector.rc
new file mode 100644
index 0000000..7faaf69
--- /dev/null
+++ b/procfs-inspector/server/com.android.car.procfsinspector.rc
@@ -0,0 +1,7 @@
+service com.android.car.procfsinspector /system/bin/com.android.car.procfsinspector
+ class core
+ user nobody
+ group readproc
+
+on boot && property:boot.car_service_created=1
+ start com.android.car.procfsinspector
diff --git a/procfs-inspector/server/directory.cpp b/procfs-inspector/server/directory.cpp
new file mode 100644
index 0000000..45dbc0a
--- /dev/null
+++ b/procfs-inspector/server/directory.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#include "directory.h"
+
+#include <android-base/stringprintf.h>
+
+procfsinspector::Directory::Directory(const char* path) {
+ if (path && path[0]) {
+ mPath.assign(path);
+ mDirectory.reset(opendir(path));
+ }
+}
+
+bool procfsinspector::Directory::Entry::isEmpty() const {
+ return mParent.empty() && mChild.empty();
+}
+
+procfsinspector::Directory::Entry::Entry(std::string parent, std::string child) :
+ mParent(parent), mChild(child) {
+ if (!isEmpty()) {
+ if (mParent.back() != '/') {
+ mParent += '/';
+ }
+ }
+}
+
+std::string procfsinspector::Directory::Entry::str() {
+ return mParent + mChild;
+}
+
+uid_t procfsinspector::Directory::Entry::getOwnerUserId() {
+ if (isEmpty()) {
+ return -1;
+ }
+ struct stat buf;
+ // fill in stat info for this entry, or return invalid UID on failure
+ if (stat(str().c_str(), &buf)) {
+ return -1;
+ }
+ return buf.st_uid;
+}
+
+procfsinspector::Directory::Entry procfsinspector::Directory::next(unsigned char type) {
+ if (auto dir = mDirectory.get()) {
+ dirent *entry = readdir(dir);
+ if (entry) {
+ // only return entries of the right type (regular file, directory, ...)
+ // but always return UNKNOWN entries as it is an allowed wildcard entry
+ if (entry->d_type == DT_UNKNOWN ||
+ type == DT_UNKNOWN ||
+ entry->d_type == type) {
+ return Entry(mPath, entry->d_name);
+ }
+ }
+ }
+
+ return Entry();
+}
diff --git a/procfs-inspector/server/directory.h b/procfs-inspector/server/directory.h
new file mode 100644
index 0000000..19528fe
--- /dev/null
+++ b/procfs-inspector/server/directory.h
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#ifndef CAR_PROCFS_DIRECTORY
+#define CAR_PROCFS_DIRECTORY
+
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+
+namespace procfsinspector {
+
+class Directory {
+public:
+ class Entry {
+ public:
+ Entry(std::string parent = "", std::string child = "");
+
+ const std::string& getChild() { return mChild; }
+ std::string str();
+
+ bool isEmpty() const;
+
+ operator bool() const {
+ return !isEmpty();
+ }
+
+ uid_t getOwnerUserId();
+
+ private:
+ std::string mParent;
+ std::string mChild;
+ };
+
+ Directory(const char* path);
+
+ Entry next(unsigned char type = DT_UNKNOWN);
+
+private:
+ class Deleter {
+ public:
+ void operator()(DIR* dir) {
+ if (dir) closedir(dir);
+ }
+ };
+ std::string mPath;
+ std::unique_ptr<DIR, Deleter> mDirectory;
+};
+
+}
+
+#endif // CAR_PROCFS_DIRECTORY
\ No newline at end of file
diff --git a/procfs-inspector/server/impl.cpp b/procfs-inspector/server/impl.cpp
new file mode 100644
index 0000000..888f77d
--- /dev/null
+++ b/procfs-inspector/server/impl.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#include "directory.h"
+#include "server.h"
+
+template<typename IntTy>
+static bool asNumber(const std::string& s, IntTy *value) {
+ IntTy v = 0;
+ for (auto&& c : s) {
+ if (c < '0' || c > '9') {
+ return false;
+ } else {
+ v = v * 10 + (c - '0');
+ }
+ }
+
+ if (value) *value = v;
+
+ return true;
+}
+
+std::vector<procfsinspector::ProcessInfo> procfsinspector::Impl::readProcessTable() {
+ std::vector<procfsinspector::ProcessInfo> processes;
+
+ Directory dir("/proc");
+ while (auto entry = dir.next()) {
+ pid_t pid;
+ if (asNumber(entry.getChild(), &pid)) {
+ processes.push_back(ProcessInfo{pid, entry.getOwnerUserId()});
+ }
+ }
+
+ return processes;
+}
diff --git a/procfs-inspector/server/main.cpp b/procfs-inspector/server/main.cpp
new file mode 100644
index 0000000..1555aa3
--- /dev/null
+++ b/procfs-inspector/server/main.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#include "server.h"
+
+#include <signal.h>
+
+#include <binder/IServiceManager.h>
+
+#include <utils/Log.h>
+
+using namespace android;
+
+int main(int, char**)
+{
+ ALOGI("starting " LOG_TAG);
+ signal(SIGPIPE, SIG_IGN);
+
+ sp<ProcessState> processSelf(ProcessState::self());
+ sp<IServiceManager> serviceManager = defaultServiceManager();
+ std::unique_ptr<procfsinspector::Impl> server(new procfsinspector::Impl());
+
+ serviceManager->addService(String16(SERVICE_NAME), server.get());
+
+ processSelf->startThreadPool();
+
+ ALOGI(LOG_TAG " started");
+
+ IPCThreadState::self()->joinThreadPool();
+
+ ALOGW(LOG_TAG " joined and going down");
+ return 0;
+}
diff --git a/procfs-inspector/server/process.cpp b/procfs-inspector/server/process.cpp
new file mode 100644
index 0000000..3c25ce2
--- /dev/null
+++ b/procfs-inspector/server/process.cpp
@@ -0,0 +1,15 @@
+#include "process.h"
+
+#include <binder/Parcel.h>
+
+status_t procfsinspector::ProcessInfo::writeToParcel(Parcel* parcel) const {
+ parcel->writeUint32(mPid);
+ parcel->writeUint32(mUid);
+ return android::OK;
+}
+
+status_t procfsinspector::ProcessInfo::readFromParcel(const Parcel* parcel) {
+ mPid = parcel->readUint32();
+ mUid = parcel->readUint32();
+ return android::OK;
+}
diff --git a/procfs-inspector/server/process.h b/procfs-inspector/server/process.h
new file mode 100644
index 0000000..75b76c7
--- /dev/null
+++ b/procfs-inspector/server/process.h
@@ -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.
+ */
+
+#ifndef CAR_PROCFS_PROCESS
+#define CAR_PROCFS_PROCESS
+
+#include <sys/types.h>
+
+#include <binder/Parcelable.h>
+
+using namespace android;
+
+namespace procfsinspector {
+ class ProcessInfo : public Parcelable {
+ public:
+ pid_t getPid() { return mPid; }
+ uid_t getUid() { return mUid; }
+
+ // default initialize to invalid values
+ ProcessInfo(pid_t pid = -1, uid_t uid = -1) : mPid(pid), mUid(uid) {}
+
+ virtual status_t writeToParcel(Parcel* parcel) const override;
+ virtual status_t readFromParcel(const Parcel* parcel) override;
+
+ private:
+ pid_t mPid;
+ uid_t mUid;
+ };
+}
+
+#endif // CAR_PROCFS_PROCESS
diff --git a/procfs-inspector/server/sepolicy/com.android.car.procfsinspector.te b/procfs-inspector/server/sepolicy/com.android.car.procfsinspector.te
new file mode 100644
index 0000000..acd7ddf
--- /dev/null
+++ b/procfs-inspector/server/sepolicy/com.android.car.procfsinspector.te
@@ -0,0 +1,6 @@
+type procfsinspector, domain, coredomain;
+type procfsinspector_exec, exec_type, file_type;
+
+init_daemon_domain(procfsinspector)
+
+allow system_app procfsinspector:binder call;
diff --git a/procfs-inspector/server/sepolicy/file_contexts b/procfs-inspector/server/sepolicy/file_contexts
new file mode 100644
index 0000000..5283993
--- /dev/null
+++ b/procfs-inspector/server/sepolicy/file_contexts
@@ -0,0 +1 @@
+/system/bin/com\.android\.car\.procfsinspector u:object_r:procfsinspector_exec:s0
diff --git a/procfs-inspector/server/server.cpp b/procfs-inspector/server/server.cpp
new file mode 100644
index 0000000..82e196f
--- /dev/null
+++ b/procfs-inspector/server/server.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#include "server.h"
+
+#include <binder/IPCThreadState.h>
+
+#include <private/android_filesystem_config.h>
+
+static bool isSystemUser() {
+ uid_t uid = IPCThreadState::self()->getCallingUid();
+ switch (uid) {
+ case AID_ROOT:
+ case AID_SYSTEM: {
+ return true;
+ } break;
+ default: {
+ ALOGE("uid %u is not root nor system - access denied", uid);
+ } break;
+ }
+ return false;
+}
+
+namespace procfsinspector {
+class BpProcfsInspector: public BpInterface<IProcfsInspector> {
+ public:
+ BpProcfsInspector(sp<IBinder> binder) : BpInterface<IProcfsInspector>(binder) {}
+
+ virtual std::vector<ProcessInfo> readProcessTable() override {
+ Parcel data, reply;
+ remote()->transact((uint32_t)IProcfsInspector::Call::READ_PROCESS_TABLE, data, &reply);
+
+ std::vector<procfsinspector::ProcessInfo> result;
+ reply.readParcelableVector(&result);
+ return result;
+ }
+
+};
+
+IMPLEMENT_META_INTERFACE(ProcfsInspector, "com.android.car.procfsinspector.IProcfsInspector");
+
+status_t Impl::onTransact(uint32_t code,
+ const Parcel& data, Parcel* reply, uint32_t flags) {
+
+ if (code == (uint32_t)IProcfsInspector::Call::READ_PROCESS_TABLE) {
+ CHECK_INTERFACE(IProcfsInspector, data, reply);
+ if (isSystemUser()) {
+ reply->writeNoException();
+ reply->writeParcelableVector(readProcessTable());
+ return NO_ERROR;
+ } else {
+ return PERMISSION_DENIED;
+ }
+ }
+
+ return BBinder::onTransact(code, data, reply, flags);
+}
+
+}
diff --git a/procfs-inspector/server/server.h b/procfs-inspector/server/server.h
new file mode 100644
index 0000000..ae529ff
--- /dev/null
+++ b/procfs-inspector/server/server.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#ifndef CAR_PROCFS_SERVER
+#define CAR_PROCFS_SERVER
+
+#define LOG_TAG "com.android.car.procfsinspector"
+#define SERVICE_NAME "com.android.car.procfsinspector"
+
+#include <vector>
+
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+#include <binder/IBinder.h>
+#include <binder/IInterface.h>
+
+#include <utils/Log.h>
+#include <utils/String16.h>
+
+#include "process.h"
+
+using namespace android;
+
+namespace procfsinspector {
+ class IProcfsInspector : public IInterface {
+ public:
+ DECLARE_META_INTERFACE(ProcfsInspector);
+
+ enum class Call : uint32_t {
+ READ_PROCESS_TABLE = IBinder::FIRST_CALL_TRANSACTION,
+ };
+
+ // API declarations start here
+ virtual std::vector<ProcessInfo> readProcessTable() = 0;
+ };
+
+ class Impl : public BnInterface<IProcfsInspector> {
+ public:
+ virtual status_t onTransact(uint32_t code,
+ const Parcel& data,
+ Parcel *reply,
+ uint32_t flags) override;
+ virtual std::vector<ProcessInfo> readProcessTable() override;
+ };
+}
+
+#endif // CAR_PROCFS_SERVER
diff --git a/service/Android.mk b/service/Android.mk
index 2b926df..8ee0147 100644
--- a/service/Android.mk
+++ b/service/Android.mk
@@ -42,6 +42,7 @@
android.hardware.automotive.vehicle-V2.0-java \
vehicle-hal-support-lib \
car-systemtest \
+ com.android.car.procfsinspector-client \
include $(BUILD_PACKAGE)
@@ -61,6 +62,7 @@
android.hardware.automotive.vehicle-V2.0-java \
vehicle-hal-support-lib \
car-systemtest \
+ com.android.car.procfsinspector-client \
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/service/AndroidManifest.xml b/service/AndroidManifest.xml
index 5c4dfaa..05073e3 100644
--- a/service/AndroidManifest.xml
+++ b/service/AndroidManifest.xml
@@ -101,6 +101,17 @@
android:protectionLevel="system|signature"
android:label="@string/car_permission_label_diag_clear"
android:description="@string/car_permission_desc_diag_clear" />
+ <permission
+ android:name="android.car.permission.VMS_PUBLISHER"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_vms_publisher"
+ android:description="@string/car_permission_desc_vms_publisher" />
+ <permission
+ android:name="android.car.permission.VMS_SUBSCRIBER"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_vms_subscriber"
+ android:description="@string/car_permission_desc_vms_subscriber" />
+
<!-- may replace this with system permission if proper one is defined. -->
<permission
android:name="android.car.permission.CONTROL_APP_BLOCKING"
@@ -143,6 +154,11 @@
android:label="@string/car_permission_car_cluster_control"
android:description="@string/car_permission_desc_car_cluster_control" />
+ <permission android:name="android.car.permission.STORAGE_MONITORING"
+ android:protectionLevel="system|signature"
+ android:label="@string/car_permission_label_storage_monitoring"
+ android:description="@string/car_permission_desc_storage_monitoring" />
+
<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 73028cb..b228000 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -60,7 +60,7 @@
<string name="instrumentClusterRendererService">android.car.cluster.sample/.SampleClusterServiceImpl</string>
- <!-- Whether to enable Avtivity blocking for safety. When Activity blocking is enabled,
+ <!-- Whether to enable Activity blocking for safety. When Activity blocking is enabled,
only whitelisted safe Activities will be allowed while car is not parked. -->
<bool name="enableActivityBlockingForSafety">true</bool>
<!-- Activity to be presented when un-safe activity is launched. Take a look at the javadoc of the
@@ -74,4 +74,35 @@
<string name="defauiltActivityWhitelist">android,com.android.systemui</string>
<!-- Default home activity -->
<string name="defaultHomeActivity">com.android.car.overview/com.android.car.overview.StreamOverviewActivity</string>
+ <!-- The com.android.car.VmsPublisherService will bind to this list of clients -->
+ <string-array translatable="false" name="vmsPublisherClients">
+ </string-array>
+ <!-- Permissions that the com.android.car.VmsPublisherService is allowed to grant to publishers -->
+ <string-array translatable="false" name="vmsSafePermissions">
+ <item>"android.permission.ACCESS_FINE_LOCATION"</item>
+ </string-array>
+
+ <!-- Hours of uptime (excluding sleep) after which a 1% increase in the wear of the flash
+ storage in the head-unit is considered as acceptable level of wear. -->
+ <integer name="acceptableHoursPerOnePercentFlashWear">70</integer>
+
+ <!-- How often (in hours of uptime excluding sleep) CarService will flush to disk information
+ about the total running time of the head-unit. A shutdown or reboot of the head-unit
+ will always cause a flush of the uptime information, regardless of this setting. -->
+ <integer name="uptimeHoursIntervalBetweenUptimeDataWrite">5</integer>
+
+ <!-- The name of an activity to be launched by CarService whenever it detects a change in the
+ level of wear of the flash storage. Value must either be an empty string, which means that
+ no activity shall be launched, or must be in the format of a flattened ComponentName and
+ reference a valid activity. It is strongly recommended that the chosen activity be
+ protected with the android.car.permission.STORAGE_MONITORING permission. -->
+ <string name="activityHandlerForFlashWearChanges">com.google.android.car.defaultstoragemonitoringcompanionapp/.MainActivity</string>
+
+ <!-- How often (in seconds) CarService will update I/O metrics from the kernel. -->
+ <integer name="ioStatsRefreshRateSeconds">60</integer>
+
+ <!-- The number of I/O metrics samples to keep in memory at one time.
+ The system will keep a sliding window of samples of this size, and allow
+ retrieval of activity this many sample windows back in time. -->
+ <integer name="ioStatsNumSamplesToStore">15</integer>
</resources>
diff --git a/service/res/values/strings.xml b/service/res/values/strings.xml
index 7f826d4..37503ab 100644
--- a/service/res/values/strings.xml
+++ b/service/res/values/strings.xml
@@ -116,4 +116,18 @@
<!-- Permission text: apps can clear diagnostic data from the car [CHAR LIMIT=NONE] -->
<string name="car_permission_desc_diag_clear">Clear diagnostic data from the car</string>
+ <!-- Permission text: apps can publish VMS data [CHAR LIMIT=NONE] -->
+ <string name="car_permission_label_vms_publisher">VMS publisher</string>
+ <!-- Permission text: apps can send VMS messages to the car [CHAR LIMIT=NONE] -->
+ <string name="car_permission_desc_vms_publisher">Publish vms messages</string>
+
+ <!-- Permission text: apps can subscribe to VMS data [CHAR LIMIT=NONE] -->
+ <string name="car_permission_label_vms_subscriber">VMS subscriber</string>
+ <!-- Permission text: apps can receive VMS messages from the car [CHAR LIMIT=NONE] -->
+ <string name="car_permission_desc_vms_subscriber">Subscribe to vms messages</string>
+
+ <!-- Permission text: apps can monitor flash storage usage [CHAR LIMIT=NONE] -->
+ <string name="car_permission_label_storage_monitoring">Flash storage monitoring</string>
+ <!-- Permission text: apps can monitor flash storage usage [CHAR LIMIT=NONE] -->
+ <string name="car_permission_desc_storage_monitoring">Monitor flash storage usage</string>
</resources>
diff --git a/service/src/com/android/car/CarAudioService.java b/service/src/com/android/car/CarAudioService.java
index f56b105..ab9d271 100644
--- a/service/src/com/android/car/CarAudioService.java
+++ b/service/src/com/android/car/CarAudioService.java
@@ -198,28 +198,32 @@
@Override
public void init() {
- AudioPolicy.Builder builder = new AudioPolicy.Builder(mContext);
- builder.setLooper(Looper.getMainLooper());
- boolean isFocusSupported = mAudioHal.isFocusSupported();
- if (isFocusSupported) {
- builder.setAudioPolicyFocusListener(mSystemFocusListener);
- FocusState currentState = FocusState.create(mAudioHal.getCurrentFocusState());
- int r = mAudioManager.requestAudioFocus(mBottomAudioFocusListener, mAttributeBottom,
- AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_FLAG_DELAY_OK);
- synchronized (mLock) {
- mCurrentFocusState = currentState;
- mCurrentAudioContexts = 0;
- }
- }
int audioHwVariant = mAudioHal.getHwVariant();
AudioRoutingPolicy audioRoutingPolicy = AudioRoutingPolicy.create(mContext, audioHwVariant);
- if (mUseDynamicRouting) {
- setupDynamicRouting(audioRoutingPolicy, builder);
- }
+
AudioPolicy audioPolicy = null;
+ boolean isFocusSupported = mAudioHal.isFocusSupported();
+
+ // Do we need to install a custom audio policy?
if (isFocusSupported || mUseDynamicRouting) {
+ AudioPolicy.Builder builder = new AudioPolicy.Builder(mContext);
+ builder.setLooper(Looper.getMainLooper());
+ if (isFocusSupported) {
+ builder.setAudioPolicyFocusListener(mSystemFocusListener);
+ FocusState currentState = FocusState.create(mAudioHal.getCurrentFocusState());
+ int r = mAudioManager.requestAudioFocus(mBottomAudioFocusListener, mAttributeBottom,
+ AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_FLAG_DELAY_OK);
+ synchronized (mLock) {
+ mCurrentFocusState = currentState;
+ mCurrentAudioContexts = 0;
+ }
+ }
+ if (mUseDynamicRouting) {
+ setupDynamicRouting(audioRoutingPolicy, builder);
+ }
audioPolicy = builder.build();
}
+
mAudioHal.setFocusListener(this);
mAudioHal.setAudioRoutingPolicy(audioRoutingPolicy);
mAudioHal.setOnParameterChangeListener(this);
diff --git a/service/src/com/android/car/CarDiagnosticService.java b/service/src/com/android/car/CarDiagnosticService.java
index 809439c..3bd9979 100644
--- a/service/src/com/android/car/CarDiagnosticService.java
+++ b/service/src/com/android/car/CarDiagnosticService.java
@@ -411,6 +411,13 @@
diagnosticCapabilities.isFreezeFrameSupported();
}
+ public boolean isSelectiveClearFreezeFramesSupported() {
+ DiagnosticCapabilities diagnosticCapabilities =
+ getDiagnosticHal().getDiagnosticCapabilities();
+ return isClearFreezeFramesSupported() &&
+ diagnosticCapabilities.isSelectiveClearFreezeFramesSupported();
+ }
+
// ICarDiagnostic implementations
@Override
@@ -441,14 +448,18 @@
@Override
public boolean clearFreezeFrames(long... timestamps) {
mDiagnosticClearPermission.assertGranted();
- if (mDiagnosticHal.getDiagnosticCapabilities().isFreezeFrameClearSupported()) {
- mFreezeFrameDiagnosticRecords.lock();
- mDiagnosticHal.clearFreezeFrames(timestamps);
- mFreezeFrameDiagnosticRecords.clearEvents();
- mFreezeFrameDiagnosticRecords.unlock();
- return true;
+ if (!isClearFreezeFramesSupported())
+ return false;
+ if (timestamps != null && timestamps.length != 0) {
+ if (!isSelectiveClearFreezeFramesSupported()) {
+ return false;
+ }
}
- return false;
+ mFreezeFrameDiagnosticRecords.lock();
+ mDiagnosticHal.clearFreezeFrames(timestamps);
+ mFreezeFrameDiagnosticRecords.clearEvents();
+ mFreezeFrameDiagnosticRecords.unlock();
+ return true;
}
/**
diff --git a/service/src/com/android/car/CarLog.java b/service/src/com/android/car/CarLog.java
index d9600d6..6ca570f 100644
--- a/service/src/com/android/car/CarLog.java
+++ b/service/src/com/android/car/CarLog.java
@@ -43,6 +43,7 @@
public static final String TAG_SYS = "CAR.SYS";
public static final String TAG_TEST = "CAR.TEST";
public static final String TAG_DIAGNOSTIC = "CAR.DIAGNOSTIC";
+ public static final String TAG_STORAGE = "CAR.STORAGE";
public static String concatTag(String tagPrefix, Class clazz) {
String tag = tagPrefix + "." + clazz.getSimpleName();
diff --git a/service/src/com/android/car/CarPowerManagementService.java b/service/src/com/android/car/CarPowerManagementService.java
index 4eb04b4..745c46d 100644
--- a/service/src/com/android/car/CarPowerManagementService.java
+++ b/service/src/com/android/car/CarPowerManagementService.java
@@ -24,6 +24,7 @@
import com.android.car.hal.PowerHalService;
import com.android.car.hal.PowerHalService.PowerState;
+import com.android.car.systeminterface.SystemInterface;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
diff --git a/service/src/com/android/car/CarService.java b/service/src/com/android/car/CarService.java
index 87e891e..497135e 100644
--- a/service/src/com/android/car/CarService.java
+++ b/service/src/com/android/car/CarService.java
@@ -31,6 +31,8 @@
import android.os.SystemProperties;
import android.util.Log;
+import com.android.car.systeminterface.SystemInterface;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.RingBufferIndices;
@@ -85,7 +87,9 @@
Log.i(CarLog.TAG_SERVICE, "Connected to " + mVehicleInterfaceName);
- mICarImpl = new ICarImpl(this, mVehicle, SystemInterface.getDefault(this),
+ mICarImpl = new ICarImpl(this,
+ mVehicle,
+ SystemInterface.Builder.defaultSystemInterface(this).build(),
mCanBusErrorNotifier);
mICarImpl.init();
SystemProperties.set("boot.car_service_created", "1");
@@ -95,6 +99,9 @@
super.onCreate();
}
+ // onDestroy is best-effort and might not get called on shutdown/reboot. As such it is not
+ // suitable for permanently saving state or other need-to-happen operation. If you have a
+ // cleanup task that you want to make sure happens on shutdown/reboot, see OnShutdownReboot.
@Override
public void onDestroy() {
Log.i(CarLog.TAG_SERVICE, "Service onDestroy");
diff --git a/service/src/com/android/car/CarStorageMonitoringService.java b/service/src/com/android/car/CarStorageMonitoringService.java
new file mode 100644
index 0000000..7404c08
--- /dev/null
+++ b/service/src/com/android/car/CarStorageMonitoringService.java
@@ -0,0 +1,338 @@
+/*
+ * 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.car.Car;
+import android.car.storagemonitoring.ICarStorageMonitoring;
+import android.car.storagemonitoring.UidIoStats;
+import android.car.storagemonitoring.UidIoStatsRecord;
+import android.car.storagemonitoring.WearEstimate;
+import android.car.storagemonitoring.WearEstimateChange;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.util.JsonWriter;
+import android.util.Log;
+import android.util.SparseArray;
+import com.android.car.internal.CarPermission;
+import com.android.car.storagemonitoring.IoStatsTracker;
+import com.android.car.storagemonitoring.UidIoStatsProvider;
+import com.android.car.storagemonitoring.WearEstimateRecord;
+import com.android.car.storagemonitoring.WearHistory;
+import com.android.car.storagemonitoring.WearInformation;
+import com.android.car.storagemonitoring.WearInformationProvider;
+import com.android.car.systeminterface.SystemInterface;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.json.JSONException;
+
+public class CarStorageMonitoringService extends ICarStorageMonitoring.Stub
+ implements CarServiceBase {
+ private static final boolean DBG = false;
+ private static final String TAG = CarLog.TAG_STORAGE;
+ private static final int MIN_WEAR_ESTIMATE_OF_CONCERN = 80;
+
+ static final String UPTIME_TRACKER_FILENAME = "service_uptime";
+ static final String WEAR_INFO_FILENAME = "wear_info";
+
+ private final WearInformationProvider[] mWearInformationProviders;
+ private final Context mContext;
+ private final File mUptimeTrackerFile;
+ private final File mWearInfoFile;
+ private final OnShutdownReboot mOnShutdownReboot;
+ private final SystemInterface mSystemInterface;
+ private final UidIoStatsProvider mUidIoStatsProvider;
+ private final SlidingWindow<SparseArray<UidIoStats>> mIoStatsSamples;
+ private final Object mIoStatsSamplesLock = new Object();
+
+ private final CarPermission mStorageMonitoringPermission;
+
+ private UptimeTracker mUptimeTracker = null;
+ private Optional<WearInformation> mWearInformation = Optional.empty();
+ private List<WearEstimateChange> mWearEstimateChanges = Collections.emptyList();
+ private List<UidIoStats> mBootIoStats = Collections.emptyList();
+ private IoStatsTracker mIoStatsTracker = null;
+ private boolean mInitialized = false;
+
+ public CarStorageMonitoringService(Context context, SystemInterface systemInterface) {
+ mContext = context;
+ Resources resources = mContext.getResources();
+ mUidIoStatsProvider = systemInterface.getUidIoStatsProvider();
+ mUptimeTrackerFile = new File(systemInterface.getFilesDir(), UPTIME_TRACKER_FILENAME);
+ mWearInfoFile = new File(systemInterface.getFilesDir(), WEAR_INFO_FILENAME);
+ mOnShutdownReboot = new OnShutdownReboot(mContext);
+ mSystemInterface = systemInterface;
+ mWearInformationProviders = systemInterface.getFlashWearInformationProviders();
+ mStorageMonitoringPermission =
+ new CarPermission(mContext, Car.PERMISSION_STORAGE_MONITORING);
+ mWearEstimateChanges = Collections.emptyList();
+ mIoStatsSamples = new SlidingWindow<>(
+ resources.getInteger(R.integer.ioStatsNumSamplesToStore));
+ systemInterface.scheduleActionForBootCompleted(this::doInitServiceIfNeeded,
+ Duration.ofSeconds(10));
+ }
+
+ private static long getUptimeSnapshotIntervalMs() {
+ return Duration.ofHours(R.integer.uptimeHoursIntervalBetweenUptimeDataWrite).toMillis();
+ }
+
+ private Optional<WearInformation> loadWearInformation() {
+ for (WearInformationProvider provider : mWearInformationProviders) {
+ WearInformation wearInfo = provider.load();
+ if (wearInfo != null) {
+ Log.d(TAG, "retrieved wear info " + wearInfo + " via provider " + provider);
+ return Optional.of(wearInfo);
+ }
+ }
+
+ Log.d(TAG, "no wear info available");
+ return Optional.empty();
+ }
+
+ private WearHistory loadWearHistory() {
+ if (mWearInfoFile.exists()) {
+ try {
+ WearHistory wearHistory = WearHistory.fromJson(mWearInfoFile);
+ Log.d(TAG, "retrieved wear history " + wearHistory);
+ return wearHistory;
+ } catch (IOException | JSONException e) {
+ Log.e(TAG, "unable to read wear info file " + mWearInfoFile, e);
+ }
+ }
+
+ Log.d(TAG, "no wear history available");
+ return new WearHistory();
+ }
+
+ // returns true iff a new event was added (and hence the history needs to be saved)
+ private boolean addEventIfNeeded(WearHistory wearHistory) {
+ if (!mWearInformation.isPresent()) return false;
+
+ WearInformation wearInformation = mWearInformation.get();
+ WearEstimate lastWearEstimate;
+ WearEstimate currentWearEstimate = wearInformation.toWearEstimate();
+
+ if (wearHistory.size() == 0) {
+ lastWearEstimate = WearEstimate.UNKNOWN_ESTIMATE;
+ } else {
+ lastWearEstimate = wearHistory.getLast().getNewWearEstimate();
+ }
+
+ if (currentWearEstimate.equals(lastWearEstimate)) return false;
+
+ WearEstimateRecord newRecord = new WearEstimateRecord(lastWearEstimate,
+ currentWearEstimate,
+ mUptimeTracker.getTotalUptime(),
+ Instant.now());
+ Log.d(TAG, "new wear record generated " + newRecord);
+ wearHistory.add(newRecord);
+ return true;
+ }
+
+ private void storeWearHistory(WearHistory wearHistory) {
+ try (JsonWriter jsonWriter = new JsonWriter(new FileWriter(mWearInfoFile))) {
+ wearHistory.writeToJson(jsonWriter);
+ } catch (IOException e) {
+ Log.e(TAG, "unable to write wear info file" + mWearInfoFile, e);
+ }
+ }
+
+ @Override
+ public void init() {
+ Log.d(TAG, "CarStorageMonitoringService init()");
+
+ mUptimeTracker = new UptimeTracker(mUptimeTrackerFile,
+ getUptimeSnapshotIntervalMs(),
+ mSystemInterface);
+ }
+
+ private void launchWearChangeActivity() {
+ final String activityPath = mContext.getResources().getString(
+ R.string.activityHandlerForFlashWearChanges);
+ if (activityPath.isEmpty()) return;
+ try {
+ final ComponentName activityComponent =
+ Objects.requireNonNull(ComponentName.unflattenFromString(activityPath));
+ Intent intent = new Intent();
+ intent.setComponent(activityComponent);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+ } catch (ActivityNotFoundException | NullPointerException e) {
+ Log.e(TAG,
+ "value of activityHandlerForFlashWearChanges invalid non-empty string " +
+ activityPath, e);
+ }
+ }
+
+ private static void logOnAdverseWearLevel(WearInformation wearInformation) {
+ if (wearInformation.preEolInfo > WearInformation.PRE_EOL_INFO_NORMAL ||
+ Math.max(wearInformation.lifetimeEstimateA,
+ wearInformation.lifetimeEstimateB) >= MIN_WEAR_ESTIMATE_OF_CONCERN) {
+ Log.w(TAG, "flash storage reached wear a level that requires attention: "
+ + wearInformation);
+ }
+ }
+
+ private void collectNewIoMetrics() {
+ mIoStatsTracker.update(mUidIoStatsProvider.load());
+ synchronized (mIoStatsSamplesLock) {
+ mIoStatsSamples.add(mIoStatsTracker.getCurrentSample());
+ }
+
+ if (DBG) {
+ SparseArray<UidIoStats> currentSample = mIoStatsTracker.getCurrentSample();
+ if (currentSample.size() == 0) {
+ Log.d(TAG, "no new I/O stat data");
+ } else {
+ SparseArrayStream.valueStream(currentSample).forEach(
+ uidIoStats -> Log.d(TAG, "updated I/O stat data: " + uidIoStats));
+ }
+ }
+ }
+
+ private synchronized void doInitServiceIfNeeded() {
+ if (mInitialized) return;
+
+ Log.d(TAG, "initializing CarStorageMonitoringService");
+
+ final Resources resources = mContext.getResources();
+
+ mWearInformation = loadWearInformation();
+
+ // TODO(egranata): can this be done lazily?
+ final WearHistory wearHistory = loadWearHistory();
+ final boolean didWearChangeHappen = addEventIfNeeded(wearHistory);
+ if (didWearChangeHappen) {
+ storeWearHistory(wearHistory);
+ }
+ Log.d(TAG, "wear history being tracked is " + wearHistory);
+ mWearEstimateChanges = wearHistory.toWearEstimateChanges(resources.getInteger(
+ R.integer.acceptableHoursPerOnePercentFlashWear));
+
+ mOnShutdownReboot.addAction((Context ctx, Intent intent) -> release());
+
+ mWearInformation.ifPresent(CarStorageMonitoringService::logOnAdverseWearLevel);
+
+ if (didWearChangeHappen) {
+ launchWearChangeActivity();
+ }
+
+ long bootUptime = mSystemInterface.getUptime();
+ mBootIoStats = SparseArrayStream.valueStream(mUidIoStatsProvider.load())
+ .map(record -> {
+ // at boot, assume all UIDs have been running for as long as the system has
+ // been up, since we don't really know any better
+ UidIoStats stats = new UidIoStats(record, bootUptime);
+ if (DBG) {
+ Log.d(TAG, "loaded boot I/O stat data: " + stats);
+ }
+ return stats;
+ }).collect(Collectors.toList());
+
+ final long newStatsDelayMs =
+ 1000L * resources.getInteger(R.integer.ioStatsRefreshRateSeconds);
+
+ mIoStatsTracker = new IoStatsTracker(mBootIoStats,
+ newStatsDelayMs,
+ mSystemInterface.getSystemStateInterface());
+
+ mSystemInterface.scheduleAction(this::collectNewIoMetrics, newStatsDelayMs);
+
+ Log.i(TAG, "CarStorageMonitoringService is up");
+
+ mInitialized = true;
+ }
+
+ @Override
+ public void release() {
+ Log.i(TAG, "tearing down CarStorageMonitoringService");
+ if (mUptimeTracker != null) {
+ mUptimeTracker.onDestroy();
+ }
+ mOnShutdownReboot.clearActions();
+ }
+
+ @Override
+ public void dump(PrintWriter writer) {
+ mStorageMonitoringPermission.assertGranted();
+ doInitServiceIfNeeded();
+
+ writer.println("*CarStorageMonitoringService*");
+ writer.println("last wear information retrieved: " +
+ mWearInformation.map(WearInformation::toString).orElse("missing"));
+ writer.println("wear change history: " +
+ mWearEstimateChanges.stream().map(WearEstimateChange::toString).reduce("",
+ (String s, String t) -> s + "\n" + t));
+ }
+
+ // ICarStorageMonitoring implementation
+
+ @Override
+ public int getPreEolIndicatorStatus() {
+ mStorageMonitoringPermission.assertGranted();
+ doInitServiceIfNeeded();
+
+ return mWearInformation.map(wi -> wi.preEolInfo)
+ .orElse(WearInformation.UNKNOWN_PRE_EOL_INFO);
+ }
+
+ @Override
+ public WearEstimate getWearEstimate() {
+ mStorageMonitoringPermission.assertGranted();
+ doInitServiceIfNeeded();
+
+ return mWearInformation.map(wi ->
+ new WearEstimate(wi.lifetimeEstimateA,wi.lifetimeEstimateB)).orElse(
+ WearEstimate.UNKNOWN_ESTIMATE);
+ }
+
+ @Override
+ public List<WearEstimateChange> getWearEstimateHistory() {
+ mStorageMonitoringPermission.assertGranted();
+ doInitServiceIfNeeded();
+
+ return mWearEstimateChanges;
+ }
+
+ @Override
+ public List<UidIoStats> getBootIoStats() {
+ mStorageMonitoringPermission.assertGranted();
+ doInitServiceIfNeeded();
+
+ return mBootIoStats;
+ }
+
+ @Override
+ public List<UidIoStats> getAggregateIoStats() {
+ mStorageMonitoringPermission.assertGranted();
+ doInitServiceIfNeeded();
+
+ return SparseArrayStream.valueStream(mIoStatsTracker.getTotal())
+ .collect(Collectors.toList());
+ }
+}
diff --git a/service/src/com/android/car/CarTestService.java b/service/src/com/android/car/CarTestService.java
index ec1606c..8c3f64d 100644
--- a/service/src/com/android/car/CarTestService.java
+++ b/service/src/com/android/car/CarTestService.java
@@ -100,7 +100,7 @@
}
if (mTokens.size() == 0) {
- mICarImpl.init();
+ CarServiceUtils.runOnMain(mICarImpl::init);
}
}
diff --git a/service/src/com/android/car/CarVolumeControllerFactory.java b/service/src/com/android/car/CarVolumeControllerFactory.java
index c83ef93..7dc3929 100644
--- a/service/src/com/android/car/CarVolumeControllerFactory.java
+++ b/service/src/com/android/car/CarVolumeControllerFactory.java
@@ -23,6 +23,7 @@
import android.media.IVolumeController;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteCallbackList;
@@ -90,8 +91,21 @@
* support volume controls.
*/
public static final class SimpleCarVolumeController extends CarVolumeController {
+ private static final String TAG = CarLog.TAG_AUDIO + ".SVolCtrl";
+ private static final int MSG_DISPLAY_SAFE_VOL_WARNING = 0;
+ private static final int MSG_VOL_CHANGED = 1;
+ private static final int MSG_MASTER_MUTE_CHANGED = 2;
+ private static final int MSG_SET_LAYOUT_DIRECTION = 3;
+ private static final int MSG_DISMISS = 4;
+ private static final int MSG_SET_ALLY_MODE = 5;
+
private final AudioManager mAudioManager;
private final Context mContext;
+ private RemoteCallbackList<IVolumeController> mVolumeCallbacks = new RemoteCallbackList<>();
+ @GuardedBy("this")
+ private IVolumeController mVolumeCallbackProxy;
+ private HandlerThread mVolumeThread;
+ private Handler mHandler;
public SimpleCarVolumeController(Context context) {
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
@@ -100,16 +114,27 @@
@Override
void init() {
+ synchronized (this) {
+ mVolumeThread = new HandlerThread(TAG);
+ mVolumeThread.start();
+ mHandler = new VolumeHandler(mVolumeThread.getLooper());
+ }
}
@Override
void release() {
+ synchronized (this) {
+ if (mVolumeThread != null) {
+ mVolumeThread.quit();
+ }
+ }
+ mVolumeCallbacks.kill();
}
@Override
public void setStreamVolume(int stream, int index, int flags) {
if (DBG) {
- Log.d(CarLog.TAG_AUDIO, "setStreamVolume " + stream + " " + index + " " + flags);
+ Log.d(TAG, "setStreamVolume " + stream + " " + index + " " + flags);
}
mAudioManager.setStreamVolume(stream, index, flags);
}
@@ -121,7 +146,13 @@
@Override
public void setVolumeController(IVolumeController controller) {
- mAudioManager.setVolumeController(controller);
+ synchronized (this) {
+ if (mVolumeCallbackProxy == null) {
+ mVolumeCallbackProxy = new VolumeControllerProxy();
+ mAudioManager.setVolumeController(mVolumeCallbackProxy);
+ }
+ }
+ mVolumeCallbacks.register(controller);
}
@Override
@@ -149,6 +180,146 @@
// nothing else to dump
}
+ private class VolumeHandler extends Handler {
+
+ public VolumeHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_DISPLAY_SAFE_VOL_WARNING:
+ int count = mVolumeCallbacks.beginBroadcast();
+ try {
+ for (int i = 0; i < count; i++) {
+ try {
+ mVolumeCallbacks.getBroadcastItem(i)
+ .displaySafeVolumeWarning(msg.arg1);
+ } catch (RemoteException ignored) {}
+ }
+ } finally {
+ mVolumeCallbacks.finishBroadcast();
+ }
+ break;
+ case MSG_VOL_CHANGED:
+ count = mVolumeCallbacks.beginBroadcast();
+ try {
+ for (int i = 0; i < count; i++) {
+ try {
+ mVolumeCallbacks.getBroadcastItem(i)
+ .volumeChanged(msg.arg1, msg.arg2);
+ } catch (RemoteException ignored) {}
+ }
+ } finally {
+ mVolumeCallbacks.finishBroadcast();
+ }
+ break;
+ case MSG_MASTER_MUTE_CHANGED:
+ count = mVolumeCallbacks.beginBroadcast();
+ try {
+ for (int i = 0; i < count; i++) {
+ try {
+ mVolumeCallbacks.getBroadcastItem(i)
+ .masterMuteChanged(msg.arg1);
+ } catch (RemoteException ignored) {}
+ }
+ } finally {
+ mVolumeCallbacks.finishBroadcast();
+ }
+ break;
+ case MSG_SET_LAYOUT_DIRECTION:
+ count = mVolumeCallbacks.beginBroadcast();
+ try {
+ for (int i = 0; i < count; i++) {
+ try {
+ mVolumeCallbacks.getBroadcastItem(i)
+ .setLayoutDirection(msg.arg1);
+ } catch (RemoteException ignored) {}
+ }
+ } finally {
+ mVolumeCallbacks.finishBroadcast();
+ }
+ break;
+ case MSG_DISMISS:
+ count = mVolumeCallbacks.beginBroadcast();
+ try {
+ for (int i = 0; i < count; i++) {
+ try {
+ mVolumeCallbacks.getBroadcastItem(i)
+ .dismiss();
+ } catch (RemoteException ignored) {}
+ }
+ } finally {
+ mVolumeCallbacks.finishBroadcast();
+ }
+ break;
+ case MSG_SET_ALLY_MODE:
+ count = mVolumeCallbacks.beginBroadcast();
+ try {
+ for (int i = 0; i < count; i++) {
+ try {
+ mVolumeCallbacks.getBroadcastItem(i)
+ .setA11yMode(msg.arg1);
+ } catch (RemoteException ignored) {}
+ }
+ } finally {
+ mVolumeCallbacks.finishBroadcast();
+ }
+ break;
+ }
+ }
+ }
+
+ private class VolumeControllerProxy extends IVolumeController.Stub {
+ @Override
+ public void displaySafeVolumeWarning(int flags) throws RemoteException {
+ synchronized (this) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPLAY_SAFE_VOL_WARNING,
+ flags, 0 /*unused*/));
+ }
+ }
+
+ @Override
+ public void volumeChanged(int streamType, int flags) throws RemoteException {
+ synchronized (this) {
+ mHandler.sendMessage(
+ mHandler.obtainMessage(MSG_VOL_CHANGED, streamType, flags));
+ }
+ }
+
+ @Override
+ public void masterMuteChanged(int flags) throws RemoteException {
+ synchronized (this) {
+ mHandler.sendMessage(
+ mHandler.obtainMessage(MSG_VOL_CHANGED, flags, 0 /*unused*/));
+ }
+ }
+
+ @Override
+ public void setLayoutDirection(int layoutDirection) throws RemoteException {
+ synchronized (this) {
+ mHandler.sendMessage(
+ mHandler.obtainMessage(MSG_VOL_CHANGED, layoutDirection, 0 /*unused*/));
+ }
+ }
+
+ @Override
+ public void dismiss() throws RemoteException {
+ synchronized (this) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_DISMISS));
+ }
+ }
+
+ @Override
+ public void setA11yMode(int mode) throws RemoteException {
+ synchronized (this) {
+ mHandler.sendMessage(
+ mHandler.obtainMessage(MSG_SET_ALLY_MODE, mode, 0 /*unused*/));
+ }
+ }
+ }
+
private void handleVolumeKeyDefault(KeyEvent event) {
if (event.getAction() != KeyEvent.ACTION_DOWN
|| interceptVolKeyBeforeDispatching(mContext)) {
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index bbb86f6..7d28d12 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -16,6 +16,7 @@
package com.android.car;
+import android.annotation.MainThread;
import android.app.UiModeManager;
import android.car.Car;
import android.car.ICar;
@@ -36,6 +37,7 @@
import com.android.car.internal.FeatureConfiguration;
import com.android.car.internal.FeatureUtil;
import com.android.car.pm.CarPackageManagerService;
+import com.android.car.systeminterface.SystemInterface;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.car.ICarServiceHelper;
import java.io.PrintWriter;
@@ -71,7 +73,12 @@
private final CarVendorExtensionService mCarVendorExtensionService;
private final CarBluetoothService mCarBluetoothService;
private final PerUserCarServiceHelper mPerUserCarServiceHelper;
- private CarDiagnosticService mCarDiagnosticService;
+ private final CarDiagnosticService mCarDiagnosticService;
+ private final CarStorageMonitoringService mCarStorageMonitoringService;
+ @FutureFeature
+ private VmsSubscriberService mVmsSubscriberService;
+ @FutureFeature
+ private VmsPublisherService mVmsPublisherService;
private final CarServiceBase[] mAllServices;
@@ -117,7 +124,11 @@
mPerUserCarServiceHelper = new PerUserCarServiceHelper(serviceContext);
mCarBluetoothService = new CarBluetoothService(serviceContext, mCarCabinService,
mCarSensorService, mPerUserCarServiceHelper);
+ mVmsSubscriberService = new VmsSubscriberService(serviceContext, mHal.getVmsHal());
+ mVmsPublisherService = new VmsPublisherService(serviceContext, mHal.getVmsHal());
mCarDiagnosticService = new CarDiagnosticService(serviceContext, mHal.getDiagnosticHal());
+ mCarStorageMonitoringService = new CarStorageMonitoringService(serviceContext,
+ systemInterface);
// Be careful with order. Service depending on other service should be inited later.
List<CarServiceBase> allServices = new ArrayList<>(Arrays.asList(
@@ -140,12 +151,16 @@
mCarVendorExtensionService,
mCarBluetoothService,
mCarDiagnosticService,
- mPerUserCarServiceHelper
+ mPerUserCarServiceHelper,
+ mCarStorageMonitoringService,
+ mVmsSubscriberService,
+ mVmsPublisherService
));
mAllServices = allServices.toArray(new CarServiceBase[0]);
}
- public void init() {
+ @MainThread
+ void init() {
traceBegin("VehicleHal.init");
mHal.init();
traceEnd();
@@ -156,7 +171,7 @@
traceEnd();
}
- public void release() {
+ void release() {
// release done in opposite order from init
for (int i = mAllServices.length - 1; i >= 0; i--) {
mAllServices[i].release();
@@ -164,7 +179,7 @@
mHal.release();
}
- public void vehicleHalReconnected(IVehicle vehicle) {
+ void vehicleHalReconnected(IVehicle vehicle) {
mHal.vehicleHalReconnected(vehicle);
for (CarServiceBase service : mAllServices) {
service.vehicleHalReconnected();
@@ -221,6 +236,9 @@
case Car.VENDOR_EXTENSION_SERVICE:
assertVendorExtensionPermission(mContext);
return mCarVendorExtensionService;
+ case Car.VMS_SUBSCRIBER_SERVICE:
+ assertVmsSubscriberPermission(mContext);
+ return mVmsSubscriberService;
case Car.TEST_SERVICE: {
assertPermission(mContext, Car.PERMISSION_CAR_TEST_SERVICE);
synchronized (this) {
@@ -232,6 +250,9 @@
}
case Car.BLUETOOTH_SERVICE:
return mCarBluetoothService;
+ case Car.STORAGE_MONITORING_SERVICE:
+ assertPermission(mContext, Car.PERMISSION_STORAGE_MONITORING);
+ return mCarStorageMonitoringService;
default:
Log.w(CarLog.TAG_SERVICE, "getCarService for unknown service:" + serviceName);
return null;
@@ -294,6 +315,16 @@
Car.PERMISSION_CAR_DIAGNOSTIC_CLEAR);
}
+ @FutureFeature
+ public static void assertVmsPublisherPermission(Context context) {
+ assertPermission(context, Car.PERMISSION_VMS_PUBLISHER);
+ }
+
+ @FutureFeature
+ public static void assertVmsSubscriberPermission(Context context) {
+ assertPermission(context, Car.PERMISSION_VMS_SUBSCRIBER);
+ }
+
public static void assertPermission(Context context, String permission) {
if (context.checkCallingOrSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("requires " + permission);
@@ -328,11 +359,13 @@
new CarShellCommand().exec(args, writer);
}
+ @MainThread
private static void traceBegin(String name) {
Slog.i(TAG, name);
mBootTiming.traceBegin(name);
}
+ @MainThread
private static void traceEnd() {
mBootTiming.traceEnd();
}
@@ -354,8 +387,8 @@
pw.println("\t Print this help text.");
pw.println("\tday-night-mode [day|night|sensor]");
pw.println("\t Force into day/night mode or restore to auto.");
- pw.println("\tinject-event zoned-boolean propertyType zone [true|false]");
- pw.println("\t Inject a Boolean HAL Event. ");
+ pw.println("\tday-night-mode [day|night|sensor]");
+ pw.println("\t Force into day/night mode or restore to auto.");
}
public void exec(String[] args, PrintWriter writer) {
diff --git a/service/src/com/android/car/OnShutdownReboot.java b/service/src/com/android/car/OnShutdownReboot.java
new file mode 100644
index 0000000..701fabf
--- /dev/null
+++ b/service/src/com/android/car/OnShutdownReboot.java
@@ -0,0 +1,68 @@
+/*
+ * 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.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.BiConsumer;
+
+/**
+ * This class allows one to register actions they want executed when the vehicle is being shutdown
+ * or rebooted.
+ *
+ * To use this class instantiate it as part of your long-lived service, and then add actions to it.
+ * Actions receive the Context and Intent that go with the shutdown/reboot action, which allows the
+ * action to differentiate the two cases, should it need to do so.
+ *
+ * The actions will run on the UI thread.
+ */
+class OnShutdownReboot {
+ private final Object mLock = new Object();
+
+ private final Context mContext;
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ for (BiConsumer<Context, Intent> action : mActions) {
+ action.accept(context, intent);
+ }
+ }
+ };
+
+ private final CopyOnWriteArrayList<BiConsumer<Context, Intent>> mActions =
+ new CopyOnWriteArrayList<>();
+
+ OnShutdownReboot(Context context) {
+ mContext = context;
+ IntentFilter shutdownFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
+ IntentFilter rebootFilter = new IntentFilter(Intent.ACTION_REBOOT);
+ mContext.registerReceiver(mReceiver, shutdownFilter);
+ mContext.registerReceiver(mReceiver, rebootFilter);
+ }
+
+ void addAction(BiConsumer<Context, Intent> action) {
+ mActions.add(action);
+ }
+
+ void clearActions() {
+ mActions.clear();
+ }
+}
diff --git a/service/src/com/android/car/SlidingWindow.java b/service/src/com/android/car/SlidingWindow.java
new file mode 100644
index 0000000..634f2eb
--- /dev/null
+++ b/service/src/com/android/car/SlidingWindow.java
@@ -0,0 +1,64 @@
+/*
+ * 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 java.util.ArrayDeque;
+import java.util.Iterator;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+
+/**
+ * This class keeps track of a limited fixed number of sample data points, correctly removing
+ * older samples as new ones are added, and it allows inspecting the samples, as well as
+ * easily answering N out of M questions.
+ */
+class SlidingWindow<T> implements Iterable<T> {
+ private final ArrayDeque<T> mElements;
+ private final int mMaxSize;
+
+ public SlidingWindow(int size) {
+ mMaxSize = size;
+ mElements = new ArrayDeque<>(mMaxSize);
+ }
+
+ public void add(T sample) {
+ if (mElements.size() == mMaxSize) {
+ mElements.removeFirst();
+ }
+ mElements.addLast(sample);
+ }
+
+ public void addAll(Iterable<T> elements) {
+ elements.forEach(this::add);
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ return mElements.iterator();
+ }
+
+ public Stream<T> stream() {
+ return mElements.stream();
+ }
+
+ public int size() {
+ return mElements.size();
+ }
+
+ public int count(Predicate<T> predicate) {
+ return (int)stream().filter(predicate).count();
+ }
+}
diff --git a/service/src/com/android/car/SparseArrayStream.java b/service/src/com/android/car/SparseArrayStream.java
new file mode 100644
index 0000000..c3e8fcd
--- /dev/null
+++ b/service/src/com/android/car/SparseArrayStream.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 com.android.car;
+
+import android.util.Pair;
+import android.util.SparseArray;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+/**
+ * Helper class that provides Stream abstractions for android.util.SparseArray
+ */
+public class SparseArrayStream {
+ public static <E> IntStream keyStream(SparseArray<E> array) {
+ return IntStream.range(0, array.size()).map(array::keyAt);
+ }
+
+ public static <E> Stream<E> valueStream(SparseArray<E> array) {
+ return IntStream.range(0, array.size()).mapToObj(array::valueAt);
+ }
+
+ public static <E> Stream<Pair<Integer, E>> pairStream(SparseArray<E> array) {
+ return IntStream.range(0, array.size()).mapToObj(
+ i -> new Pair<>(array.keyAt(i), array.valueAt(i)));
+ }
+}
diff --git a/service/src/com/android/car/SystemActivityMonitoringService.java b/service/src/com/android/car/SystemActivityMonitoringService.java
index 2ff4e0e..4744ae5 100644
--- a/service/src/com/android/car/SystemActivityMonitoringService.java
+++ b/service/src/com/android/car/SystemActivityMonitoringService.java
@@ -208,7 +208,12 @@
}
int focusedStackId = -1;
try {
- focusedStackId = mAm.getFocusedStackId();
+ // TODO(b/66955160): Someone on the Auto-team should probably re-work the code in the
+ // synchronized block below based on this new API.
+ final StackInfo focusedStackInfo = mAm.getFocusedStackInfo();
+ if (focusedStackInfo != null) {
+ focusedStackId = focusedStackInfo.stackId;
+ }
} catch (RemoteException e) {
Log.e(CarLog.TAG_AM, "cannot getFocusedStackId", e);
return;
@@ -251,16 +256,9 @@
}
public StackInfo getFocusedStackForTopActivity(ComponentName activity) {
- int focusedStackId = -1;
- try {
- focusedStackId = mAm.getFocusedStackId();
- } catch (RemoteException e) {
- Log.e(CarLog.TAG_AM, "cannot getFocusedStackId", e);
- return null;
- }
StackInfo focusedStack;
try {
- focusedStack = mAm.getStackInfo(focusedStackId);
+ focusedStack = mAm.getFocusedStackInfo();
} catch (RemoteException e) {
Log.e(CarLog.TAG_AM, "cannot getFocusedStackId", e);
return null;
diff --git a/service/src/com/android/car/SystemInterface.java b/service/src/com/android/car/SystemInterface.java
deleted file mode 100644
index 6311d80..0000000
--- a/service/src/com/android/car/SystemInterface.java
+++ /dev/null
@@ -1,191 +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.android.car;
-
-import android.content.Context;
-import android.hardware.display.DisplayManager;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.Display;
-
-/**
- * Interface to abstract all system interaction.
- */
-public abstract class SystemInterface {
- public abstract void setDisplayState(boolean on);
- public abstract void releaseAllWakeLocks();
- public abstract void shutdown();
- public abstract void enterDeepSleep(int wakeupTimeSec);
- public abstract void switchToPartialWakeLock();
- public abstract void switchToFullWakeLock();
- public abstract void startDisplayStateMonitoring(CarPowerManagementService service);
- public abstract void stopDisplayStateMonitoring();
- public abstract boolean isSystemSupportingDeepSleep();
- public abstract boolean isWakeupCausedByTimer();
-
-
- public static SystemInterface getDefault(Context context) {
- return new SystemInterfaceImpl(context);
- }
-
- private static class SystemInterfaceImpl extends SystemInterface {
- private final PowerManager mPowerManager;
- private final DisplayManager mDisplayManager;
- private final WakeLock mFullWakeLock;
- private final WakeLock mPartialWakeLock;
- private final DisplayStateListener mDisplayListener;
- private CarPowerManagementService mService;
- private boolean mDisplayStateSet;
-
- private SystemInterfaceImpl(Context context) {
- mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
- mFullWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK,
- CarLog.TAG_POWER);
- mPartialWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- CarLog.TAG_POWER);
- mDisplayListener = new DisplayStateListener();
- }
-
- @Override
- public void startDisplayStateMonitoring(CarPowerManagementService service) {
- synchronized (this) {
- mService = service;
- mDisplayStateSet = isMainDisplayOn();
- }
- mDisplayManager.registerDisplayListener(mDisplayListener, service.getHandler());
- }
-
- @Override
- public void stopDisplayStateMonitoring() {
- mDisplayManager.unregisterDisplayListener(mDisplayListener);
- }
-
- @Override
- public void setDisplayState(boolean on) {
- synchronized (this) {
- mDisplayStateSet = on;
- }
- if (on) {
- switchToFullWakeLock();
- Log.i(CarLog.TAG_POWER, "on display");
- mPowerManager.wakeUp(SystemClock.uptimeMillis());
- } else {
- switchToPartialWakeLock();
- Log.i(CarLog.TAG_POWER, "off display");
- mPowerManager.goToSleep(SystemClock.uptimeMillis());
- }
- }
-
- private boolean isMainDisplayOn() {
- Display disp = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
- return disp.getState() == Display.STATE_ON;
- }
-
- @Override
- public void shutdown() {
- mPowerManager.shutdown(false /* no confirm*/, null, true /* true */);
- }
-
- @Override
- public void enterDeepSleep(int wakeupTimeSec) {
- //TODO set wake up time, bug: 32061842
- mPowerManager.goToSleep(SystemClock.uptimeMillis(),
- PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN,
- PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
- }
-
- @Override
- public boolean isSystemSupportingDeepSleep() {
- //TODO should return by checking some kernel suspend control sysfs, bug: 32061842
- return false;
- }
-
- @Override
- public void switchToPartialWakeLock() {
- if (!mPartialWakeLock.isHeld()) {
- mPartialWakeLock.acquire();
- }
- if (mFullWakeLock.isHeld()) {
- mFullWakeLock.release();
- }
- }
-
- @Override
- public void switchToFullWakeLock() {
- if (!mFullWakeLock.isHeld()) {
- mFullWakeLock.acquire();
- }
- if (mPartialWakeLock.isHeld()) {
- mPartialWakeLock.release();
- }
- }
-
- @Override
- public void releaseAllWakeLocks() {
- if (mPartialWakeLock.isHeld()) {
- mPartialWakeLock.release();
- }
- if (mFullWakeLock.isHeld()) {
- mFullWakeLock.release();
- }
- }
-
- @Override
- public boolean isWakeupCausedByTimer() {
- //TODO bug: 32061842, check wake up reason and do necessary operation information should
- // come from kernel. it can be either power on or wake up for maintenance
- // power on will involve GPIO trigger from power controller
- // its own wakeup will involve timer expiration.
- return false;
- }
-
- private void handleMainDisplayChanged() {
- boolean isOn = isMainDisplayOn();
- CarPowerManagementService service;
- synchronized (this) {
- if (mDisplayStateSet == isOn) { // same as what is set
- return;
- }
- service = mService;
- }
- service.handleMainDisplayChanged(isOn);
- }
-
- private class DisplayStateListener implements DisplayManager.DisplayListener {
-
- @Override
- public void onDisplayAdded(int displayId) {
- //ignore
- }
-
- @Override
- public void onDisplayChanged(int displayId) {
- if (displayId == Display.DEFAULT_DISPLAY) {
- handleMainDisplayChanged();
- }
- }
-
- @Override
- public void onDisplayRemoved(int displayId) {
- //ignore
- }
- }
- }
-}
diff --git a/service/src/com/android/car/UptimeTracker.java b/service/src/com/android/car/UptimeTracker.java
new file mode 100644
index 0000000..2a1d26e
--- /dev/null
+++ b/service/src/com/android/car/UptimeTracker.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 com.android.car;
+
+import android.util.JsonReader;
+import android.util.JsonWriter;
+import android.util.Log;
+
+import com.android.car.systeminterface.SystemInterface;
+
+import com.android.car.systeminterface.TimeInterface;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * A class that can keep track of how long its instances are alive for.
+ *
+ * It can be used as a helper object to track the lifetime of system components, e.g.
+ *
+ * class InterestingService {
+ * private UptimeTracker mTracker;
+ *
+ * public void onCreate() {
+ * mTracker = new UptimeTracker(
+ * "/storage/emulated/0/Android/data/interestingservice.uptime", 1 hour);
+ * mTracker.onCreate();
+ * }
+ *
+ * public void onDestroy() {
+ * mTracker.onDestroy();
+ * }
+ * }
+ *
+ * Now it's possible to know how long InterestingService has been alive in the system by querying
+ * mTracker.getTotalUptime(). Because this data is stored to disk, the uptime is maintained across
+ * process and system reboot boundaries. It is possible to configure periodic snapshot points to
+ * ensure that crashes do not cause more than a certain amount of uptime to go untracked.
+ */
+public class UptimeTracker {
+ /**
+ * In order to prevent excessive wear-out of the storage, do not allow snapshots to happen
+ * more frequently than this value
+ */
+ public static final long MINIMUM_SNAPSHOT_INTERVAL_MS = 60 * 60 * 1000;
+
+ /**
+ * The default snapshot interval if none is given
+ */
+ private static long DEFAULT_SNAPSHOT_INTERVAL_MS = 5 * 60 * 60 * 1000; // 5 hours
+
+ private final Object mLock = new Object();
+
+ /**
+ * The file that uptime metrics are stored to
+ */
+ private File mUptimeFile;
+
+ /**
+ * The uptime value retrieved from mUptimeFile
+ */
+ private Optional<Long> mHistoricalUptime;
+
+ /**
+ * Last value of elapsedRealTime read from the system
+ */
+ private long mLastRealTimeSnapshot;
+
+ /**
+ * The source of real-time and scheduling
+ */
+ private TimeInterface mTimeInterface;
+
+ public UptimeTracker(File file) {
+ this(file, DEFAULT_SNAPSHOT_INTERVAL_MS);
+ }
+
+ public UptimeTracker(File file, long snapshotInterval) {
+ this(file, snapshotInterval, new TimeInterface.DefaultImpl());
+ }
+
+ UptimeTracker(File file, long snapshotInterval, SystemInterface systemInterface) {
+ this(file, snapshotInterval, systemInterface.getTimeInterface());
+ }
+
+ // This constructor allows one to replace the source of time-based truths with
+ // a mock version. This is mostly useful for testing purposes.
+ @VisibleForTesting
+ UptimeTracker(File file,
+ long snapshotInterval,
+ TimeInterface timeInterface) {
+ snapshotInterval = Math.max(snapshotInterval, MINIMUM_SNAPSHOT_INTERVAL_MS);
+ mUptimeFile = Objects.requireNonNull(file);
+ mTimeInterface = timeInterface;
+ mLastRealTimeSnapshot = mTimeInterface.getUptime(TimeInterface.EXCLUDE_DEEP_SLEEP_TIME);
+ mHistoricalUptime = Optional.empty();
+
+ mTimeInterface.scheduleAction(this::flushSnapshot, snapshotInterval);
+ }
+
+ void onDestroy() {
+ synchronized (mLock) {
+ if (mTimeInterface != null) {
+ mTimeInterface.cancelAllActions();
+ }
+ flushSnapshot();
+ mTimeInterface = null;
+ mUptimeFile = null;
+ }
+ }
+
+ /**
+ * Return the total amount of uptime that has been observed, in milliseconds.
+ *
+ * This is the sum of the uptime stored on disk + the uptime seen since the last snapshot.
+ */
+ long getTotalUptime() {
+ synchronized (mLock) {
+ if (mTimeInterface == null) {
+ return 0;
+ }
+ return getHistoricalUptimeLocked() + (
+ mTimeInterface.getUptime(TimeInterface.EXCLUDE_DEEP_SLEEP_TIME)
+ - mLastRealTimeSnapshot);
+ }
+ }
+
+ private long getHistoricalUptimeLocked() {
+ if (!mHistoricalUptime.isPresent() && mUptimeFile != null) {
+ try {
+ JsonReader reader = new JsonReader(new FileReader(mUptimeFile));
+ reader.beginObject();
+ if (!reader.nextName().equals("uptime")) {
+ throw new IllegalArgumentException(
+ mUptimeFile + " is not in a valid format");
+ } else {
+ mHistoricalUptime = Optional.of(reader.nextLong());
+ }
+ reader.endObject();
+ reader.close();
+ } catch (IllegalArgumentException | IOException e) {
+ Log.w(CarLog.TAG_SERVICE, "unable to read historical uptime data", e);
+ mHistoricalUptime = Optional.empty();
+ }
+ }
+ return mHistoricalUptime.orElse(0L);
+ }
+
+ private void flushSnapshot() {
+ synchronized (mLock) {
+ if (mUptimeFile == null) {
+ return;
+ }
+ try {
+ long newUptime = getTotalUptime();
+ mHistoricalUptime = Optional.of(newUptime);
+ mLastRealTimeSnapshot = mTimeInterface.getUptime(
+ TimeInterface.EXCLUDE_DEEP_SLEEP_TIME);
+
+ JsonWriter writer = new JsonWriter(new FileWriter(mUptimeFile));
+ writer.beginObject();
+ writer.name("uptime");
+ writer.value(newUptime);
+ writer.endObject();
+ writer.close();
+ } catch (IOException e) {
+ Log.w(CarLog.TAG_SERVICE, "unable to write historical uptime data", e);
+ }
+ }
+ }
+}
diff --git a/service/src/com/android/car/VmsLayersAvailability.java b/service/src/com/android/car/VmsLayersAvailability.java
new file mode 100644
index 0000000..4c730c1
--- /dev/null
+++ b/service/src/com/android/car/VmsLayersAvailability.java
@@ -0,0 +1,200 @@
+/*
+ * 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.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;
+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 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.
+ *
+ * @hide
+ */
+
+public class VmsLayersAvailability {
+
+ private static final boolean DBG = true;
+ private static final String TAG = "VmsLayersAvailability";
+
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private final Map<VmsLayer, Set<Set<VmsLayer>>> mPotentialLayersAndDependencies =
+ new HashMap<>();
+ @GuardedBy("mLock")
+ private Set<VmsAssociatedLayer> mAvailableAssociatedLayers = Collections.EMPTY_SET;
+ @GuardedBy("mLock")
+ private Set<VmsAssociatedLayer> mUnavailableAssociatedLayers = Collections.EMPTY_SET;
+ @GuardedBy("mLock")
+ private Map<VmsLayer, Set<Integer>> mPotentialLayersAndPublishers = new HashMap<>();
+
+ /**
+ * Setting the current layers offerings as reported by publishers.
+ */
+ public void setPublishersOffering(Collection<VmsLayersOffering> publishersLayersOfferings) {
+ synchronized (mLock) {
+ reset();
+
+ 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);
+ if (curDependencies == null) {
+ curDependencies = new HashSet<>();
+ mPotentialLayersAndDependencies.put(layer, curDependencies);
+ }
+ curDependencies.add(dependency.getDependencies());
+ }
+ }
+ calculateLayers();
+ }
+ }
+
+ /**
+ * Returns a collection of all the layers which may be published.
+ */
+ public Set<VmsAssociatedLayer> getAvailableLayers() {
+ synchronized (mLock) {
+ return mAvailableAssociatedLayers;
+ }
+ }
+
+ /**
+ * Returns a collection of all the layers which publishers could have published if the
+ * dependencies were satisfied.
+ */
+ public Set<VmsAssociatedLayer> getUnavailableLayers() {
+ synchronized (mLock) {
+ return mUnavailableAssociatedLayers;
+ }
+ }
+
+ private void reset() {
+ synchronized (mLock) {
+ mPotentialLayersAndDependencies.clear();
+ mPotentialLayersAndPublishers.clear();
+ mAvailableAssociatedLayers = Collections.EMPTY_SET;
+ mUnavailableAssociatedLayers = Collections.EMPTY_SET;
+ }
+ }
+
+ private void calculateLayers() {
+ synchronized (mLock) {
+ Set<VmsLayer> availableLayersSet = new HashSet<>();
+ Set<VmsLayer> cyclicAvoidanceAuxiliarySet = new HashSet<>();
+
+ for (VmsLayer layer : mPotentialLayersAndDependencies.keySet()) {
+ addLayerToAvailabilityCalculationLocked(layer,
+ availableLayersSet,
+ cyclicAvoidanceAuxiliarySet);
+ }
+
+ 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 void addLayerToAvailabilityCalculationLocked(VmsLayer layer,
+ Set<VmsLayer> currentAvailableLayers,
+ Set<VmsLayer> cyclicAvoidanceSet) {
+ if (DBG) {
+ 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;
+ }
+ // If there is no offering for this layer we're done.
+ if (!mPotentialLayersAndDependencies.containsKey(layer)) {
+ return;
+ }
+ // Avoid cyclic dependency.
+ 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;
+ }
+ // Add the layer to cyclic avoidance set
+ cyclicAvoidanceSet.add(layer);
+
+ boolean isSupported = true;
+ for (VmsLayer dependency : dependencies) {
+ addLayerToAvailabilityCalculationLocked(dependency,
+ currentAvailableLayers,
+ cyclicAvoidanceSet);
+
+ if (!currentAvailableLayers.contains(dependency)) {
+ isSupported = false;
+ break;
+ }
+ }
+ cyclicAvoidanceSet.remove(layer);
+
+ if (isSupported) {
+ currentAvailableLayers.add(layer);
+ return;
+ }
+ }
+ return;
+ }
+}
diff --git a/service/src/com/android/car/VmsPublisherService.java b/service/src/com/android/car/VmsPublisherService.java
new file mode 100644
index 0000000..2d2acf6
--- /dev/null
+++ b/service/src/com/android/car/VmsPublisherService.java
@@ -0,0 +1,338 @@
+/*
+ * 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.SystemApi;
+import android.car.vms.IVmsSubscriberClient;
+import android.car.vms.IVmsPublisherClient;
+import android.car.vms.IVmsPublisherService;
+import android.car.vms.VmsLayer;
+import android.car.vms.VmsLayersOffering;
+import android.car.vms.VmsSubscriptionState;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.car.hal.VmsHalService;
+import com.android.internal.annotations.GuardedBy;
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Receives HAL updates by implementing VmsHalService.VmsHalListener.
+ * Binds to publishers and configures them to use this service.
+ * Notifies publishers of subscription changes.
+ *
+ * @hide
+ */
+@SystemApi
+public class VmsPublisherService extends IVmsPublisherService.Stub
+ implements CarServiceBase, VmsHalService.VmsHalPublisherListener {
+ private static final boolean DBG = true;
+ private static final String TAG = "VmsPublisherService";
+
+ private final Context mContext;
+ private final VmsHalService mHal;
+ private final VmsPublisherManager mPublisherManager;
+ private Set<String> mSafePermissions;
+
+ public VmsPublisherService(Context context, VmsHalService hal) {
+ mContext = context;
+ mHal = hal;
+ mPublisherManager = new VmsPublisherManager(this);
+ }
+
+ // Implements CarServiceBase interface.
+ @Override
+ public void init() {
+ mHal.addPublisherListener(this);
+ // Load permissions that can be granted to publishers.
+ mSafePermissions = new HashSet<>(
+ Arrays.asList(mContext.getResources().getStringArray(R.array.vmsSafePermissions)));
+ // Launch publishers.
+ String[] publisherNames = mContext.getResources().getStringArray(
+ R.array.vmsPublisherClients);
+ for (String publisherName : publisherNames) {
+ if (TextUtils.isEmpty(publisherName)) {
+ Log.e(TAG, "empty publisher name");
+ continue;
+ }
+ ComponentName name = ComponentName.unflattenFromString(publisherName);
+ if (name == null) {
+ Log.e(TAG, "invalid publisher name: " + publisherName);
+ }
+ mPublisherManager.bind(name);
+ }
+ }
+
+ @Override
+ public void release() {
+ mPublisherManager.release();
+ mHal.removePublisherListener(this);
+ }
+
+ @Override
+ public void dump(PrintWriter writer) {
+ }
+
+ @Override
+ public void setLayersOffering(IBinder token, VmsLayersOffering offering) {
+ mHal.setPublisherLayersOffering(token, offering);
+ }
+
+ // Implements IVmsPublisherService interface.
+ @Override
+ 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.getSubscribersForLayerFromPublisher(layer, publisherId);
+
+ if (DBG) {
+ Log.d(TAG, "Number of subscribed apps: " + listeners.size());
+ }
+ for (IVmsSubscriberClient listener : listeners) {
+ try {
+ listener.onVmsMessageReceived(layer, payload);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to publish to listener: " + listener);
+ }
+ }
+
+ // Send the message to HAL
+ if (mHal.isHalSubscribed(layer)) {
+ Log.d(TAG, "HAL is subscribed");
+ mHal.setDataMessage(layer, payload);
+ } else {
+ Log.d(TAG, "HAL is NOT subscribed");
+ }
+ }
+
+ @Override
+ public VmsSubscriptionState getSubscriptions() {
+ ICarImpl.assertVmsPublisherPermission(mContext);
+ return mHal.getSubscriptionState();
+ }
+
+ @Override
+ public int getPublisherId(byte[] publisherInfo) {
+ ICarImpl.assertVmsPublisherPermission(mContext);
+ return mHal.getPublisherId(publisherInfo);
+ }
+
+ // Implements VmsHalListener interface
+ /**
+ * This method is only invoked by VmsHalService.notifyPublishers which is synchronized.
+ * Therefore this method only sees a non-decreasing sequence.
+ */
+ @Override
+ public void onChange(VmsSubscriptionState subscriptionState) {
+ // Send the message to application listeners.
+ for (IVmsPublisherClient client : mPublisherManager.getClients()) {
+ try {
+ client.onVmsSubscriptionChange(subscriptionState);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to send notification to: " + client, ex);
+ }
+ }
+ }
+
+ /**
+ * Keeps track of publishers that are using this service.
+ */
+ private static class VmsPublisherManager {
+ /**
+ * Allows to modify mPublisherMap and mPublisherConnectionMap as a single unit.
+ */
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private final Map<String, PublisherConnection> mPublisherConnectionMap = new HashMap<>();
+ @GuardedBy("mLock")
+ private final Map<String, IVmsPublisherClient> mPublisherMap = new HashMap<>();
+ private final WeakReference<VmsPublisherService> mPublisherService;
+
+ public VmsPublisherManager(VmsPublisherService publisherService) {
+ mPublisherService = new WeakReference<>(publisherService);
+ }
+
+ /**
+ * Tries to bind to a publisher.
+ *
+ * @param name publisher component name (e.g. android.car.vms.logger/.LoggingService).
+ */
+ public void bind(ComponentName name) {
+ VmsPublisherService publisherService = mPublisherService.get();
+ if (publisherService == null) return;
+ String publisherName = name.flattenToString();
+ if (DBG) {
+ Log.d(TAG, "binding to: " + publisherName);
+ }
+ synchronized (mLock) {
+ if (mPublisherConnectionMap.containsKey(publisherName)) {
+ // Already registered, nothing to do.
+ return;
+ }
+ grantPermissions(name);
+ Intent intent = new Intent();
+ intent.setComponent(name);
+ PublisherConnection connection = new PublisherConnection();
+ if (publisherService.mContext.bindServiceAsUser(intent, connection,
+ Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
+ mPublisherConnectionMap.put(publisherName, connection);
+ } else {
+ Log.e(TAG, "unable to bind to: " + publisherName);
+ }
+ }
+ }
+
+ /**
+ * Removes the publisher and associated connection.
+ *
+ * @param name publisher component name (e.g. android.car.vms.Logger).
+ */
+ public void unbind(ComponentName name) {
+ VmsPublisherService publisherService = mPublisherService.get();
+ if (publisherService == null) return;
+ String publisherName = name.flattenToString();
+ if (DBG) {
+ Log.d(TAG, "unbinding from: " + publisherName);
+ }
+ synchronized (mLock) {
+ boolean found = mPublisherMap.remove(publisherName) != null;
+ if (found) {
+ PublisherConnection connection = mPublisherConnectionMap.get(publisherName);
+ publisherService.mContext.unbindService(connection);
+ mPublisherConnectionMap.remove(publisherName);
+ } else {
+ Log.e(TAG, "unbind: unknown publisher." + publisherName);
+ }
+ }
+ }
+
+ /**
+ * Returns the list of publishers currently registered.
+ *
+ * @return list of publishers.
+ */
+ public List<IVmsPublisherClient> getClients() {
+ synchronized (mLock) {
+ return new ArrayList<>(mPublisherMap.values());
+ }
+ }
+
+ public void release() {
+ VmsPublisherService publisherService = mPublisherService.get();
+ if (publisherService == null) return;
+ for (PublisherConnection connection : mPublisherConnectionMap.values()) {
+ publisherService.mContext.unbindService(connection);
+ }
+ mPublisherConnectionMap.clear();
+ mPublisherMap.clear();
+ }
+
+ private void grantPermissions(ComponentName component) {
+ VmsPublisherService publisherService = mPublisherService.get();
+ if (publisherService == null) return;
+ final PackageManager packageManager = publisherService.mContext.getPackageManager();
+ final String packageName = component.getPackageName();
+ PackageInfo packageInfo;
+ try {
+ packageInfo = packageManager.getPackageInfo(packageName,
+ PackageManager.GET_PERMISSIONS);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Error getting package info for " + packageName, e);
+ return;
+ }
+ if (packageInfo.requestedPermissions == null) return;
+ for (String permission : packageInfo.requestedPermissions) {
+ if (!publisherService.mSafePermissions.contains(permission)) {
+ continue;
+ }
+ if (packageManager.checkPermission(permission, packageName)
+ == PackageManager.PERMISSION_GRANTED) {
+ continue;
+ }
+ try {
+ packageManager.grantRuntimePermission(packageName, permission,
+ UserHandle.SYSTEM);
+ Log.d(TAG, "Permission " + permission + " granted to " + packageName);
+ } catch (SecurityException | IllegalArgumentException e) {
+ Log.e(TAG, "Error while trying to grant " + permission + " to " + packageName,
+ e);
+ }
+ }
+ }
+
+ class PublisherConnection implements ServiceConnection {
+
+ private final IBinder mToken = new Binder();
+
+ /**
+ * Once the service binds to a publisher service, the publisher binder is added to
+ * mPublisherMap
+ * and the publisher is configured to use this service.
+ */
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder binder) {
+ VmsPublisherService publisherService = mPublisherService.get();
+ if (publisherService == null) return;
+ if (DBG) {
+ Log.d(TAG, "onServiceConnected, name: " + name + ", binder: " + binder);
+ }
+ IVmsPublisherClient service = IVmsPublisherClient.Stub.asInterface(binder);
+ synchronized (mLock) {
+ mPublisherMap.put(name.flattenToString(), service);
+ }
+ try {
+ service.setVmsPublisherService(mToken, publisherService);
+ } catch (RemoteException e) {
+ Log.e(TAG, "unable to configure publisher: " + name);
+ }
+ }
+
+ /**
+ * Tries to rebind to the publisher service.
+ */
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ String publisherName = name.flattenToString();
+ Log.d(TAG, "onServiceDisconnected, name: " + publisherName);
+ VmsPublisherManager.this.unbind(name);
+ VmsPublisherManager.this.bind(name);
+ }
+ }
+ }
+}
diff --git a/service/src/com/android/car/VmsPublishersInfo.java b/service/src/com/android/car/VmsPublishersInfo.java
new file mode 100644
index 0000000..0f73e5c
--- /dev/null
+++ b/service/src/com/android/car/VmsPublishersInfo.java
@@ -0,0 +1,95 @@
+/*
+ * 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 java.util.HashMap;
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+import com.android.internal.annotations.GuardedBy;
+import android.util.Log;
+
+public class VmsPublishersInfo {
+ private static final String TAG = "VmsPublishersInfo";
+ private static final boolean DBG = true;
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private final Map<InfoWrapper, Integer> mPublishersIds = new HashMap();
+ @GuardedBy("mLock")
+ private final Map<Integer, byte[]> mPublishersInfo = new HashMap();
+
+ private static class InfoWrapper {
+ private final byte[] mInfo;
+
+ public InfoWrapper(byte[] info) {
+ mInfo = info;
+ }
+
+ public byte[] getInfo() {
+ return mInfo;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof InfoWrapper)) {
+ return false;
+ }
+ InfoWrapper p = (InfoWrapper) o;
+ return Arrays.equals(this.mInfo, p.mInfo);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(mInfo);
+ }
+ }
+
+ /**
+ * Returns the ID associated with the publisher info. When called for the first time for a
+ * publisher info will store the info and assign an ID
+ */
+ public int getIdForInfo(byte[] publisherInfo) {
+ Integer publisherId;
+ InfoWrapper wrappedPublisherInfo = new InfoWrapper(publisherInfo);
+ synchronized (mLock) {
+ maybeAddPublisherInfoLocked(wrappedPublisherInfo);
+ publisherId = mPublishersIds.get(wrappedPublisherInfo);
+ }
+ if (DBG) {
+ Log.i(TAG, "Publisher ID is: " + publisherId);
+ }
+ return publisherId;
+ }
+
+ public byte[] getPublisherInfo(int publisherId) {
+ synchronized (mLock) {
+ return mPublishersInfo.get(publisherId).clone();
+ }
+ }
+
+ private void maybeAddPublisherInfoLocked(InfoWrapper wrappedPublisherInfo) {
+ if (!mPublishersIds.containsKey(wrappedPublisherInfo)) {
+ // Assign ID to the info
+ Integer publisherId = mPublishersIds.size();
+
+ mPublishersIds.put(wrappedPublisherInfo, publisherId);
+ mPublishersInfo.put(publisherId, wrappedPublisherInfo.getInfo());
+ }
+ }
+}
+
diff --git a/service/src/com/android/car/VmsRouting.java b/service/src/com/android/car/VmsRouting.java
new file mode 100644
index 0000000..fa29a27
--- /dev/null
+++ b/service/src/com/android/car/VmsRouting.java
@@ -0,0 +1,413 @@
+/*
+ * 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.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:
+ * + Subscriptions to data messages of individual layer + version.
+ * + Subscriptions to all data messages.
+ * + HAL subscriptions to layer + version.
+ */
+
+public class VmsRouting {
+ private final Object mLock = new Object();
+ // A map of Layer + Version to subscribers.
+ @GuardedBy("mLock")
+ private Map<VmsLayer, Set<IVmsSubscriberClient>> mLayerSubscriptions = new HashMap<>();
+
+ @GuardedBy("mLock")
+ 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 subscriber subscription to data messages from a VMS layer.
+ *
+ * @param subscriber a VMS subscriber.
+ * @param layer the layer subscribing to.
+ */
+ 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 subscribers for layer and version.
+ Set<IVmsSubscriberClient> subscribers = mLayerSubscriptions.get(layer);
+
+ if (subscribers == null) {
+ subscribers = new HashSet<>();
+ mLayerSubscriptions.put(layer, subscribers);
+ }
+ // Add the subscriber to the list.
+ subscribers.add(subscriber);
+ VmsOperationRecorder.get().addSubscription(mSequenceNumber, layer);
+ }
+ }
+
+ /**
+ * Add a subscriber subscription to all data messages.
+ *
+ * @param subscriber a VMS subscriber.
+ */
+ public void addSubscription(IVmsSubscriberClient subscriber) {
+ synchronized (mLock) {
+ ++mSequenceNumber;
+ 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);
+ }
+ }
+
+ /**
+ * Remove a subscription for a layer + version and make sure to remove the key if there are no
+ * more subscribers.
+ *
+ * @param subscriber to remove.
+ * @param layer of the subscription.
+ */
+ public void removeSubscription(IVmsSubscriberClient subscriber, VmsLayer layer) {
+ synchronized (mLock) {
+ ++mSequenceNumber;
+ Set<IVmsSubscriberClient> subscribers = mLayerSubscriptions.get(layer);
+
+ // If there are no subscribers we are done.
+ if (subscribers == null) {
+ return;
+ }
+ subscribers.remove(subscriber);
+ VmsOperationRecorder.get().removeSubscription(mSequenceNumber, layer);
+
+ // If there are no more subscribers then remove the list.
+ if (subscribers.isEmpty()) {
+ mLayerSubscriptions.remove(layer);
+ }
+ }
+ }
+
+ /**
+ * Remove a subscriber subscription to all data messages.
+ *
+ * @param subscriber a VMS subscriber.
+ */
+ public void removeSubscription(IVmsSubscriberClient subscriber) {
+ synchronized (mLock) {
+ ++mSequenceNumber;
+ 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 subscriber a VMS subscriber.
+ */
+ public void removeDeadSubscriber(IVmsSubscriberClient subscriber) {
+ synchronized (mLock) {
+ // Remove the subscriber from all the routes.
+ for (VmsLayer layer : mLayerSubscriptions.keySet()) {
+ removeSubscription(subscriber, layer);
+ }
+ // Remove the subscriber from the loggers.
+ removeSubscription(subscriber);
+ }
+ }
+
+ /**
+ * 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 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> getSubscribersForLayerFromPublisher(VmsLayer layer,
+ int publisherId) {
+ Set<IVmsSubscriberClient> subscribers = new HashSet<>();
+ synchronized (mLock) {
+ // Add the subscribers which explicitly subscribed to this layer
+ if (mLayerSubscriptions.containsKey(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.
+ subscribers.addAll(mPromiscuousSubscribers);
+ }
+ return subscribers;
+ }
+
+ /**
+ * Returns a list with all the subscribers.
+ */
+ public Set<IVmsSubscriberClient> getAllSubscribers() {
+ Set<IVmsSubscriberClient> subscribers = new HashSet<>();
+ synchronized (mLock) {
+ for (VmsLayer layer : mLayerSubscriptions.keySet()) {
+ subscribers.addAll(mLayerSubscriptions.get(layer));
+ }
+ // Add the promiscuous subscribers.
+ subscribers.addAll(mPromiscuousSubscribers);
+ }
+ return subscribers;
+ }
+
+ /**
+ * 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 containsSubscriber(IVmsSubscriberClient subscriber) {
+ synchronized (mLock) {
+ // Check if subscriber is subscribed to a layer.
+ for (Set<IVmsSubscriberClient> layerSubscribers : mLayerSubscriptions.values()) {
+ if (layerSubscribers.contains(subscriber)) {
+ return true;
+ }
+ }
+ // 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.
+ */
+ public boolean isHalSubscribed(VmsLayer layer) {
+ synchronized (mLock) {
+ return mHalSubscriptions.contains(layer);
+ }
+ }
+
+ /**
+ * checks if there are subscribers to a layer.
+ *
+ * @param layer
+ * @return true if there are subscribers to layer.
+ */
+ public boolean hasLayerSubscriptions(VmsLayer layer) {
+ synchronized (mLock) {
+ return mLayerSubscriptions.containsKey(layer) || mHalSubscriptions.contains(layer);
+ }
+ }
+
+ /**
+ * 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) {
+ Set<VmsLayer> layers = new HashSet<>();
+ layers.addAll(mLayerSubscriptions.keySet());
+ layers.addAll(mHalSubscriptions);
+
+
+ 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
new file mode 100644
index 0000000..5031fb4
--- /dev/null
+++ b/service/src/com/android/car/VmsSubscriberService.java
@@ -0,0 +1,353 @@
+/*
+ * 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.SystemApi;
+import android.car.Car;
+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;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.car.hal.VmsHalService;
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+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;
+
+/**
+ * + Receives HAL updates by implementing VmsHalService.VmsHalListener.
+ * + Offers subscriber/publisher services by implementing IVmsService.Stub.
+ */
+@SystemApi
+public class VmsSubscriberService extends IVmsSubscriberService.Stub
+ implements CarServiceBase, VmsHalService.VmsHalSubscriberListener {
+ private static final boolean DBG = true;
+ private static final String PERMISSION = Car.PERMISSION_VMS_SUBSCRIBER;
+ private static final String TAG = "VmsSubscriberService";
+
+ private final Context mContext;
+ private final VmsHalService mHal;
+
+ @GuardedBy("mSubscriberServiceLock")
+ private final VmsSubscribersManager mSubscribersManager = new VmsSubscribersManager();
+ private final Object mSubscriberServiceLock = new Object();
+
+ /**
+ * Keeps track of subscribers of this service.
+ */
+ class VmsSubscribersManager {
+ /**
+ * 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> mSubscriberMap = new HashMap<>();
+
+ class ListenerDeathRecipient implements IBinder.DeathRecipient {
+ private IBinder mSubscriberBinder;
+
+ ListenerDeathRecipient(IBinder subscriberBinder) {
+ mSubscriberBinder = subscriberBinder;
+ }
+
+ /**
+ * Listener died. Remove it from this service.
+ */
+ @Override
+ public void binderDied() {
+ if (DBG) {
+ Log.d(TAG, "binderDied " + mSubscriberBinder);
+ }
+
+ // Get the Listener from the Binder
+ IVmsSubscriberClient subscriber = mSubscriberMap.get(mSubscriberBinder);
+
+ // 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 subscriber");
+
+ }
+
+ // Remove binder
+ VmsSubscribersManager.this.removeListener(mSubscriberBinder);
+ }
+
+ void release() {
+ mSubscriberBinder.unlinkToDeath(this, 0);
+ }
+ }
+
+ public void release() {
+ for (ListenerDeathRecipient recipient : mListenerDeathRecipientMap.values()) {
+ recipient.release();
+ }
+ mListenerDeathRecipientMap.clear();
+ mSubscriberMap.clear();
+ }
+
+ /**
+ * Adds the subscriber and a death recipient associated to it.
+ *
+ * @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
+ * subscriber.
+ */
+ public void add(IVmsSubscriberClient subscriber) {
+ ICarImpl.assertPermission(mContext, PERMISSION);
+ if (subscriber == null) {
+ Log.e(TAG, "register: subscriber is null.");
+ throw new IllegalArgumentException("subscriber cannot be null.");
+ }
+ if (DBG) {
+ Log.d(TAG, "register: " + subscriber);
+ }
+ IBinder subscriberBinder = subscriber.asBinder();
+ synchronized (mListenerManagerLock) {
+ if (mSubscriberMap.containsKey(subscriberBinder)) {
+ // Already registered, nothing to do.
+ return;
+ }
+ ListenerDeathRecipient deathRecipient =
+ new ListenerDeathRecipient(subscriberBinder);
+ try {
+ 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(subscriberBinder, deathRecipient);
+ mSubscriberMap.put(subscriberBinder, subscriber);
+ }
+ }
+
+ /**
+ * Removes the subscriber and associated death recipient.
+ *
+ * @param subscriber to be removed.
+ * @throws IllegalArgumentException if subscriber is null.
+ */
+ public void remove(IVmsSubscriberClient subscriber) {
+ if (DBG) {
+ Log.d(TAG, "unregisterListener");
+ }
+ ICarImpl.assertPermission(mContext, PERMISSION);
+ if (subscriber == null) {
+ Log.e(TAG, "unregister: subscriber is null.");
+ throw new IllegalArgumentException("Listener is null");
+ }
+ IBinder subscriberBinder = subscriber.asBinder();
+ removeListener(subscriberBinder);
+ }
+
+ // 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 = mSubscriberMap.remove(subscriberBinder) != null;
+ if (found) {
+ mListenerDeathRecipientMap.get(subscriberBinder).release();
+ mListenerDeathRecipientMap.remove(subscriberBinder);
+ } else {
+ Log.e(TAG, "removeListener: subscriber was not previously registered.");
+ }
+ }
+ }
+
+ /**
+ * Returns list of subscribers currently registered.
+ *
+ * @return list of subscribers.
+ */
+ public List<IVmsSubscriberClient> getListeners() {
+ synchronized (mListenerManagerLock) {
+ return new ArrayList<>(mSubscriberMap.values());
+ }
+ }
+ }
+
+ public VmsSubscriberService(Context context, VmsHalService hal) {
+ mContext = context;
+ mHal = hal;
+ }
+
+ // Implements CarServiceBase interface.
+ @Override
+ public void init() {
+ mHal.addSubscriberListener(this);
+ }
+
+ @Override
+ public void release() {
+ mSubscribersManager.release();
+ mHal.removeSubscriberListener(this);
+ }
+
+ @Override
+ public void dump(PrintWriter writer) {
+ }
+
+ // Implements IVmsService interface.
+ @Override
+ public void addVmsSubscriberToNotifications(IVmsSubscriberClient subscriber) {
+ synchronized (mSubscriberServiceLock) {
+ // 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(subscriber, layer);
+ }
+ }
+
+ @Override
+ public void removeVmsSubscriber(IVmsSubscriberClient subscriber, VmsLayer layer) {
+ synchronized (mSubscriberServiceLock) {
+ // Remove the subscription.
+ mHal.removeSubscription(subscriber, layer);
+ }
+ }
+
+ @Override
+ public void addVmsSubscriberToPublisher(IVmsSubscriberClient subscriber,
+ VmsLayer layer,
+ int publisherId) {
+ synchronized (mSubscriberServiceLock) {
+ // Add the subscriber so it can subscribe.
+ mSubscribersManager.add(subscriber);
+
+ // Add the subscription for the layer.
+ mHal.addSubscription(subscriber, layer, publisherId);
+ }
+ }
+
+ @Override
+ public void removeVmsSubscriberToPublisher(IVmsSubscriberClient subscriber,
+ VmsLayer layer,
+ int publisherId) {
+ synchronized (mSubscriberServiceLock) {
+ // Remove the subscription.
+ mHal.removeSubscription(subscriber, layer, publisherId);
+ }
+ }
+
+ @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);
+ }
+ }
+
+ @Override
+ public byte[] getPublisherInfo(int publisherId) {
+ synchronized (mSubscriberServiceLock) {
+ return mHal.getPublisherInfo(publisherId);
+ }
+ }
+
+ @Override
+ public List<VmsLayer> getAvailableLayers() {
+ //TODO(asafro): return the list of available layers once logic is implemented.
+ return Collections.emptyList();
+ }
+
+ // Implements VmsHalSubscriberListener interface
+ @Override
+ public void onDataMessage(VmsLayer layer, int publisherId, byte[] payload) {
+ if (DBG) {
+ Log.d(TAG, "Publishing a message for layer: " + layer);
+ }
+
+ Set<IVmsSubscriberClient> subscribers =
+ mHal.getSubscribersForLayerFromPublisher(layer, publisherId);
+
+ for (IVmsSubscriberClient subscriber : subscribers) {
+ try {
+ subscriber.onVmsMessageReceived(layer, payload);
+ } 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, "onVmsMessageReceived calling failed: ", e);
+ }
+ }
+ }
+
+ @Override
+ public void onLayersAvaiabilityChange(List<VmsAssociatedLayer> availableLayers) {
+ if (DBG) {
+ Log.d(TAG, "Publishing layers availability change: " + availableLayers);
+ }
+
+ Set<IVmsSubscriberClient> subscribers;
+ synchronized (mSubscriberServiceLock) {
+ subscribers = new HashSet<>(mSubscribersManager.getListeners());
+ }
+
+ for (IVmsSubscriberClient subscriber : subscribers) {
+ try {
+ 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, "onLayersAvailabilityChanged calling failed: ", e);
+ }
+ }
+ }
+}
diff --git a/service/src/com/android/car/hal/DiagnosticHalService.java b/service/src/com/android/car/hal/DiagnosticHalService.java
index 4ff0ada..bcbae7a 100644
--- a/service/src/com/android/car/hal/DiagnosticHalService.java
+++ b/service/src/com/android/car/hal/DiagnosticHalService.java
@@ -42,6 +42,8 @@
* higher-level semantic information
*/
public class DiagnosticHalService extends SensorHalServiceBase {
+ static final int OBD2_SELECTIVE_FRAME_CLEAR = 1;
+
public static class DiagnosticCapabilities {
private final CopyOnWriteArraySet<Integer> mProperties = new CopyOnWriteArraySet<>();
@@ -69,6 +71,10 @@
return isSupported(VehicleProperty.OBD2_FREEZE_FRAME_CLEAR);
}
+ public boolean isSelectiveClearFreezeFramesSupported() {
+ return isSupported(OBD2_SELECTIVE_FRAME_CLEAR);
+ }
+
void clear() {
mProperties.clear();
}
@@ -102,6 +108,17 @@
return propConfig.prop;
case VehicleProperty.OBD2_FREEZE_FRAME_CLEAR:
mDiagnosticCapabilities.setSupported(propConfig.prop);
+ Log.i(CarLog.TAG_DIAGNOSTIC, String.format(
+ "configArray for OBD2_FREEZE_FRAME_CLEAR is %s", propConfig.configArray));
+ if (propConfig.configArray.size() < 1) {
+ Log.e(CarLog.TAG_DIAGNOSTIC, String.format(
+ "property 0x%x does not specify whether it supports selective " +
+ "clearing of freeze frames. assuming it does not.", propConfig.prop));
+ } else {
+ if (propConfig.configArray.get(0) == 1) {
+ mDiagnosticCapabilities.setSupported(OBD2_SELECTIVE_FRAME_CLEAR);
+ }
+ }
return propConfig.prop;
default:
return SENSOR_TYPE_INVALID;
diff --git a/service/src/com/android/car/hal/VehicleHal.java b/service/src/com/android/car/hal/VehicleHal.java
index 44c81d7..3c54bfc 100644
--- a/service/src/com/android/car/hal/VehicleHal.java
+++ b/service/src/com/android/car/hal/VehicleHal.java
@@ -80,6 +80,8 @@
private final VendorExtensionHalService mVendorExtensionHal;
private DiagnosticHalService mDiagnosticHal = null;
+ @FutureFeature
+ private VmsHalService mVmsHal;
/** Might be re-assigned if Vehicle HAL is reconnected. */
@@ -106,6 +108,7 @@
mHvacHal = new HvacHalService(this);
mInputHal = new InputHalService(this);
mVendorExtensionHal = new VendorExtensionHalService(this);
+ mVmsHal = new VmsHalService(this);
mDiagnosticHal = new DiagnosticHalService(this);
mAllServices.addAll(Arrays.asList(mPowerHal,
mSensorHal,
@@ -116,7 +119,8 @@
mHvacHal,
mInputHal,
mVendorExtensionHal,
- mDiagnosticHal));
+ mDiagnosticHal,
+ mVmsHal));
mHalClient = new HalClient(vehicle, mHandlerThread.getLooper(), this /*IVehicleCallback*/);
}
@@ -124,7 +128,7 @@
/** Dummy version only for testing */
@VisibleForTesting
public VehicleHal(PowerHalService powerHal, SensorHalService sensorHal, InfoHalService infoHal,
- AudioHalService audioHal, CabinHalService cabinHal,
+ AudioHalService audioHal, CabinHalService cabinHal, DiagnosticHalService diagnosticHal,
RadioHalService radioHal, HvacHalService hvacHal, HalClient halClient) {
mHandlerThread = null;
mPowerHal = powerHal;
@@ -132,33 +136,14 @@
mInfoHal = infoHal;
mAudioHal = audioHal;
mCabinHal = cabinHal;
+ mDiagnosticHal = diagnosticHal;
mRadioHal = radioHal;
mHvacHal = hvacHal;
mInputHal = null;
mVendorExtensionHal = null;
- mDiagnosticHal = null;
-
+ mVmsHal = null;
mHalClient = halClient;
- }
-
- /** Dummy version only for testing */
- @VisibleForTesting
- public VehicleHal(PowerHalService powerHal, SensorHalService sensorHal, InfoHalService infoHal,
- AudioHalService audioHal, CabinHalService cabinHal, DiagnosticHalService diagnosticHal,
- RadioHalService radioHal, HvacHalService hvacHal, HalClient halClient) {
- mHandlerThread = null;
- mPowerHal = powerHal;
- mSensorHal = sensorHal;
- mInfoHal = infoHal;
- mAudioHal = audioHal;
- mCabinHal = cabinHal;
- mDiagnosticHal = diagnosticHal;
- mRadioHal = radioHal;
- mHvacHal = hvacHal;
- mInputHal = null;
- mVendorExtensionHal = null;
- mHalClient = halClient;
- mDiagnosticHal = diagnosticHal;
+ mDiagnosticHal = diagnosticHal;
}
public void vehicleHalReconnected(IVehicle vehicle) {
@@ -268,6 +253,9 @@
return mVendorExtensionHal;
}
+ @FutureFeature
+ public VmsHalService getVmsHal() { return mVmsHal; }
+
private void assertServiceOwnerLocked(HalServiceBase service, int property) {
if (service != mPropertyHandlers.get(property)) {
throw new IllegalArgumentException("Property 0x" + toHexString(property)
diff --git a/service/src/com/android/car/hal/VmsHalService.java b/service/src/com/android/car/hal/VmsHalService.java
new file mode 100644
index 0000000..36da390
--- /dev/null
+++ b/service/src/com/android/car/hal/VmsHalService.java
@@ -0,0 +1,850 @@
+/*
+ * 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.hal;
+
+import static com.android.car.CarServiceUtils.toByteArray;
+import static java.lang.Integer.toHexString;
+
+import android.annotation.SystemApi;
+import android.car.VehicleAreaType;
+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_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;
+import com.android.car.CarLog;
+import com.android.car.VmsLayersAvailability;
+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;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * This is a glue layer between the VehicleHal and the VmsService. It sends VMS properties back and
+ * forth.
+ */
+@SystemApi
+public class VmsHalService extends HalServiceBase {
+
+ private static final boolean DBG = true;
+ 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<>();
+ private CopyOnWriteArrayList<VmsHalSubscriberListener> mSubscriberListeners =
+ new CopyOnWriteArrayList<>();
+
+ private final IBinder mHalPublisherToken = new Binder();
+ private final VehicleHal mVehicleHal;
+
+ private final Object mLock = new Object();
+ private final VmsRouting mRouting = new VmsRouting();
+ @GuardedBy("mLock")
+ private final Map<IBinder, VmsLayersOffering> mOfferings = new HashMap<>();
+ @GuardedBy("mLock")
+ private final VmsLayersAvailability mAvailableLayers = new VmsLayersAvailability();
+ private final VmsPublishersInfo mPublishersInfo = new VmsPublishersInfo();
+
+ /**
+ * The VmsPublisherService implements this interface to receive data from the HAL.
+ */
+ public interface VmsHalPublisherListener {
+ void onChange(VmsSubscriptionState subscriptionState);
+ }
+
+ /**
+ * The VmsSubscriberService implements this interface to receive data from the HAL.
+ */
+ public interface VmsHalSubscriberListener {
+ // Notifies the listener on a data Message from a publisher.
+ void onDataMessage(VmsLayer layer, int publisherId, byte[] payload);
+
+ // Notifies the listener on a change in available layers.
+ void onLayersAvaiabilityChange(List<VmsAssociatedLayer> availableLayers);
+ }
+
+ /**
+ * The VmsService implements this interface to receive data from the HAL.
+ */
+ protected VmsHalService(VehicleHal vehicleHal) {
+ mVehicleHal = vehicleHal;
+ if (DBG) {
+ Log.d(TAG, "started VmsHalService!");
+ }
+ }
+
+ public void addPublisherListener(VmsHalPublisherListener listener) {
+ mPublisherListeners.add(listener);
+ }
+
+ public void addSubscriberListener(VmsHalSubscriberListener listener) {
+ mSubscriberListeners.add(listener);
+ }
+
+ public void removePublisherListener(VmsHalPublisherListener listener) {
+ mPublisherListeners.remove(listener);
+ }
+
+ public void removeSubscriberListener(VmsHalSubscriberListener listener) {
+ mSubscriberListeners.remove(listener);
+ }
+
+ public void addSubscription(IVmsSubscriberClient listener, VmsLayer layer) {
+ boolean firstSubscriptionForLayer = false;
+ synchronized (mLock) {
+ // Check if publishers need to be notified about this change in subscriptions.
+ firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer);
+
+ // Add the listeners subscription to the layer
+ mRouting.addSubscription(listener, layer);
+ }
+ if (firstSubscriptionForLayer) {
+ notifyHalPublishers(layer, true);
+ notifyClientPublishers();
+ }
+ }
+
+ public void removeSubscription(IVmsSubscriberClient listener, VmsLayer layer) {
+ boolean layerHasSubscribers = true;
+ synchronized (mLock) {
+ 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.removeSubscription(listener, layer);
+
+ // Check if publishers need to be notified about this change in subscriptions.
+ layerHasSubscribers = mRouting.hasLayerSubscriptions(layer);
+ }
+ if (!layerHasSubscribers) {
+ notifyHalPublishers(layer, false);
+ notifyClientPublishers();
+ }
+ }
+
+ public void addSubscription(IVmsSubscriberClient listener) {
+ synchronized (mLock) {
+ mRouting.addSubscription(listener);
+ }
+ }
+
+ public void removeSubscription(IVmsSubscriberClient listener) {
+ synchronized (mLock) {
+ mRouting.removeSubscription(listener);
+ }
+ }
+
+ public void addSubscription(IVmsSubscriberClient listener, VmsLayer layer, int publisherId) {
+ boolean firstSubscriptionForLayer = false;
+ 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.addSubscription(listener, layer, publisherId);
+ }
+ if (firstSubscriptionForLayer) {
+ notifyHalPublishers(layer, true);
+ notifyClientPublishers();
+ }
+ }
+
+ public void removeSubscription(IVmsSubscriberClient listener, VmsLayer layer, int publisherId) {
+ boolean layerHasSubscribers = true;
+ synchronized (mLock) {
+ 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 void removeDeadSubscriber(IVmsSubscriberClient listener) {
+ synchronized (mLock) {
+ 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();
+ }
+ }
+
+ public boolean isHalSubscribed(VmsLayer layer) {
+ synchronized (mLock) {
+ return mRouting.isHalSubscribed(layer);
+ }
+ }
+
+ public VmsSubscriptionState getSubscriptionState() {
+ synchronized (mLock) {
+ return mRouting.getSubscriptionState();
+ }
+ }
+
+ /**
+ * Assigns an idempotent ID for publisherInfo and stores it. The idempotency in this case means
+ * that the same publisherInfo will always, within a trip of the vehicle, return the same ID.
+ * 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 getPublisherId(byte[] publisherInfo) {
+ if (DBG) {
+ Log.i(TAG, "Getting publisher static ID");
+ }
+ synchronized (mLock) {
+ return mPublishersInfo.getIdForInfo(publisherInfo);
+ }
+ }
+
+ public byte[] getPublisherInfo(int publisherId) {
+ if (DBG) {
+ Log.i(TAG, "Getting information for publisher ID: " + publisherId);
+ }
+ synchronized (mLock) {
+ return mPublishersInfo.getPublisherInfo(publisherId);
+ }
+ }
+
+ private void addHalSubscription(VmsLayer layer) {
+ boolean firstSubscriptionForLayer = true;
+ synchronized (mLock) {
+ // Check if publishers need to be notified about this change in subscriptions.
+ firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer);
+
+ // Add the listeners subscription to the layer
+ mRouting.addHalSubscription(layer);
+ }
+ if (firstSubscriptionForLayer) {
+ notifyHalPublishers(layer, true);
+ notifyClientPublishers();
+ }
+ }
+
+ 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)) {
+ Log.i(TAG, "Trying to remove a layer with no subscription: " + layer);
+ return;
+ }
+
+ // Remove the listeners subscription to the layer
+ mRouting.removeHalSubscription(layer);
+
+ // Check if publishers need to be notified about this change in subscriptions.
+ layerHasSubscribers = mRouting.hasLayerSubscriptions(layer);
+ }
+ if (!layerHasSubscribers) {
+ notifyHalPublishers(layer, false);
+ notifyClientPublishers();
+ }
+ }
+
+ public void removeHalSubscriptionFromPublisher(VmsLayer layer, int publisherId) {
+ boolean layerHasSubscribers = true;
+ synchronized (mLock) {
+ 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 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();
+ }
+ notifyOfAvailabilityChange(availableLayers);
+ }
+
+ public Set<VmsAssociatedLayer> getAvailableLayers() {
+ //TODO(b/36872877): wrap available layers in VmsAvailabilityState similar to VmsSubscriptionState.
+ synchronized (mLock) {
+ return mAvailableLayers.getAvailableLayers();
+ }
+ }
+
+ /**
+ * Notify all the publishers and the HAL on subscription changes regardless of who triggered
+ * the change.
+ *
+ * @param layer layer which is being subscribed to or unsubscribed from.
+ * @param hasSubscribers indicates if the notification is for subscription or unsubscription.
+ */
+ 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.
+ // They should ignore any notification with a timestamp that is older than the most
+ // recent timestamp they have seen.
+ listener.onChange(getSubscriptionState());
+ }
+ }
+
+ /**
+ * Notify all the subscribers and the HAL on layers availability change.
+ *
+ * @param availableLayers the layers which publishers claim they made publish.
+ */
+ private void notifyOfAvailabilityChange(Set<VmsAssociatedLayer> availableLayers) {
+ // notify the HAL
+ notifyAvailabilityChangeToHal(availableLayers);
+
+ // Notify the App subscribers
+ for (VmsHalSubscriberListener listener : mSubscriberListeners) {
+ listener.onLayersAvaiabilityChange(new ArrayList<>(availableLayers));
+ }
+ }
+
+ @Override
+ public void init() {
+ if (DBG) {
+ Log.d(TAG, "init()");
+ }
+ if (mIsSupported) {
+ mVehicleHal.subscribeProperty(this, HAL_PROPERTY_ID);
+ }
+ }
+
+ @Override
+ public void release() {
+ if (DBG) {
+ Log.d(TAG, "release()");
+ }
+ if (mIsSupported) {
+ mVehicleHal.unsubscribeProperty(this, HAL_PROPERTY_ID);
+ }
+ mPublisherListeners.clear();
+ mSubscriberListeners.clear();
+ }
+
+ @Override
+ public Collection<VehiclePropConfig> takeSupportedProperties(
+ Collection<VehiclePropConfig> allProperties) {
+ List<VehiclePropConfig> taken = new LinkedList<>();
+ for (VehiclePropConfig p : allProperties) {
+ if (p.prop == HAL_PROPERTY_ID) {
+ taken.add(p);
+ mIsSupported = true;
+ if (DBG) {
+ Log.d(TAG, "takeSupportedProperties: " + toHexString(p.prop));
+ }
+ break;
+ }
+ }
+ return taken;
+ }
+
+ /**
+ * Consumes/produces HAL messages. The format of these messages is defined in:
+ * hardware/interfaces/automotive/vehicle/2.1/types.hal
+ */
+ @Override
+ public void handleHalEvents(List<VehiclePropValue> values) {
+ if (DBG) {
+ Log.d(TAG, "Handling a VMS property change");
+ }
+ for (VehiclePropValue v : values) {
+ ArrayList<Integer> vec = v.value.int32Values;
+ int messageType = vec.get(VmsBaseMessageIntegerValuesIndex.MESSAGE_TYPE);
+
+ if (DBG) {
+ Log.d(TAG, "Handling VMS message type: " + messageType);
+ }
+ switch (messageType) {
+ case VmsMessageType.DATA:
+ handleDataEvent(vec, toByteArray(v.value.bytes));
+ break;
+ case VmsMessageType.SUBSCRIBE:
+ handleSubscribeEvent(vec);
+ break;
+ 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:
+ handleHalAvailabilityRequestEvent();
+ break;
+ case VmsMessageType.SUBSCRIPTIONS_REQUEST:
+ handleSubscriptionRequestEvent();
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected message type: " + messageType);
+ }
+ }
+ }
+
+ 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) {
+ VmsLayer vmsLayer = parseVmsLayerFromDataMessageIntegerValues(integerValues);
+ int publisherId = parsePublisherIdFromDataMessageIntegerValues(integerValues);
+ if (DBG) {
+ Log.d(TAG, "Handling a data event for Layer: " + vmsLayer);
+ }
+
+ // Send the message.
+ for (VmsHalSubscriberListener listener : mSubscriberListeners) {
+ listener.onDataMessage(vmsLayer, publisherId, payload);
+ }
+ }
+
+ /**
+ * Subscribe message format:
+ * <ul>
+ * <li>Message type.
+ * <li>Layer id.
+ * <li>Layer version.
+ * <li>Layer subtype.
+ * </ul>
+ */
+ private void handleSubscribeEvent(List<Integer> integerValues) {
+ VmsLayer vmsLayer = parseVmsLayerFromSimpleMessageIntegerValues(integerValues);
+ if (DBG) {
+ Log.d(TAG, "Handling a subscribe event for Layer: " + vmsLayer);
+ }
+ 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);
+ }
+
+ /**
+ * Unsubscribe message format:
+ * <ul>
+ * <li>Message type.
+ * <li>Layer id.
+ * <li>Layer version.
+ * </ul>
+ */
+ private void handleUnsubscribeEvent(List<Integer> integerValues) {
+ VmsLayer vmsLayer = parseVmsLayerFromSimpleMessageIntegerValues(integerValues);
+ if (DBG) {
+ Log.d(TAG, "Handling an unsubscribe event for Layer: " + vmsLayer);
+ }
+ 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/subtype/version.
+ * </ul>
+ * </ul>
+ */
+ private void handleOfferingEvent(List<Integer> integerValues) {
+ int publisherId = integerValues.get(VmsOfferingMessageIntegerValuesIndex.PUBLISHER_ID);
+ int numLayersDependencies =
+ integerValues.get(
+ VmsOfferingMessageIntegerValuesIndex.NUMBER_OF_OFFERS);
+ int idx = VmsOfferingMessageIntegerValuesIndex.OFFERING_START;
+
+ Set<VmsLayerDependency> offeredLayers = new HashSet<>();
+
+ // An offering is layerId, LayerVersion, LayerType, NumDeps, <LayerId, LayerVersion> X NumDeps.
+ for (int i = 0; i < numLayersDependencies; i++) {
+ VmsLayer offeredLayer = parseVmsLayerFromIndex(integerValues, idx);
+ idx += NUM_INTEGERS_IN_VMS_LAYER;
+
+ int numDependenciesForLayer = integerValues.get(idx++);
+ if (numDependenciesForLayer == 0) {
+ offeredLayers.add(new VmsLayerDependency(offeredLayer));
+ } else {
+ Set<VmsLayer> dependencies = new HashSet<>();
+
+ for (int j = 0; j < numDependenciesForLayer; j++) {
+ 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, publisherId);
+ synchronized (mLock) {
+ updateOffering(mHalPublisherToken, offering);
+ VmsOperationRecorder.get().setHalPublisherLayersOffering(offering);
+ }
+ }
+
+ /**
+ * Availability message format:
+ * <ul>
+ * <li>Message type.
+ * <li>Number of layers.
+ * <li>Layer type/subtype/version.
+ * </ul>
+ */
+ private void handleHalAvailabilityRequestEvent() {
+ synchronized (mLock) {
+ Collection<VmsAssociatedLayer> availableLayers = mAvailableLayers.getAvailableLayers();
+ VehiclePropValue vehiclePropertyValue =
+ toAvailabilityUpdateVehiclePropValue(
+ availableLayers,
+ VmsMessageType.AVAILABILITY_RESPONSE);
+
+ setPropertyValue(vehiclePropertyValue);
+ }
+ }
+
+ /**
+ * VmsSubscriptionRequestFormat:
+ * <ul>
+ * <li>Message type.
+ * </ul>
+ * <p>
+ * VmsSubscriptionResponseFormat:
+ * <ul>
+ * <li>Message type.
+ * <li>Sequence number.
+ * <li>Number of layers.
+ * <li>Layer type/subtype/version.
+ * </ul>
+ */
+ private void handleSubscriptionRequestEvent() {
+ VmsSubscriptionState subscription = getSubscriptionState();
+ VehiclePropValue vehicleProp =
+ toTypedVmsVehiclePropValue(VmsMessageType.SUBSCRIPTIONS_RESPONSE);
+ VehiclePropValue.RawValue v = vehicleProp.value;
+ v.int32Values.add(subscription.getSequenceNumber());
+ 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.getType());
+ v.int32Values.add(layer.getSubtype());
+ v.int32Values.add(layer.getVersion());
+ }
+ setPropertyValue(vehicleProp);
+ }
+
+ private void updateOffering(IBinder publisherToken, VmsLayersOffering offering) {
+ Set<VmsAssociatedLayer> availableLayers = Collections.EMPTY_SET;
+ synchronized (mLock) {
+ mOfferings.put(publisherToken, offering);
+
+ // Update layers availability.
+ mAvailableLayers.setPublishersOffering(mOfferings.values());
+
+ availableLayers = mAvailableLayers.getAvailableLayers();
+ }
+ notifyOfAvailabilityChange(availableLayers);
+ }
+
+ @Override
+ public void dump(PrintWriter writer) {
+ writer.println(TAG);
+ writer.println("VmsProperty " + (mIsSupported ? "" : "not") + " supported.");
+ }
+
+ /**
+ * Updates the VMS HAL property with the given value.
+ *
+ * @param layer layer data to update the hal property.
+ * @param hasSubscribers if it is a subscribe or unsubscribe message.
+ * @return true if the call to the HAL to update the property was successful.
+ */
+ public boolean setSubscriptionRequest(VmsLayer layer, boolean hasSubscribers) {
+ 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 =
+ 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 notifyAvailabilityChangeToHal(Collection<VmsAssociatedLayer> availableLayers) {
+ VehiclePropValue vehiclePropertyValue =
+ toAvailabilityUpdateVehiclePropValue(
+ availableLayers,
+ VmsMessageType.AVAILABILITY_CHANGE);
+
+ return setPropertyValue(vehiclePropertyValue);
+ }
+
+ public boolean setPropertyValue(VehiclePropValue vehiclePropertyValue) {
+ try {
+ mVehicleHal.set(vehiclePropertyValue);
+ return true;
+ } catch (PropertyTimeoutException e) {
+ Log.e(CarLog.TAG_PROPERTY, "set, property not ready 0x" + toHexString(HAL_PROPERTY_ID));
+ }
+ return false;
+ }
+
+ private static VehiclePropValue toTypedVmsVehiclePropValue(int messageType) {
+ VehiclePropValue vehicleProp = new VehiclePropValue();
+ vehicleProp.prop = HAL_PROPERTY_ID;
+ vehicleProp.areaId = VehicleAreaType.VEHICLE_AREA_TYPE_NONE;
+ VehiclePropValue.RawValue v = vehicleProp.value;
+
+ v.int32Values.add(messageType);
+ return vehicleProp;
+ }
+
+ /**
+ * 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.getType());
+ v.int32Values.add(layer.getSubtype());
+ v.int32Values.add(layer.getVersion());
+ return vehicleProp;
+ }
+
+ 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;
+
+ }
+
+ private static void populateAvailabilityPropValueFields(
+ Collection<VmsAssociatedLayer> availableAssociatedLayers,
+ VehiclePropValue vehicleProp) {
+ VehiclePropValue.RawValue v = vehicleProp.value;
+ int numLayers = availableAssociatedLayers.size();
+ v.int32Values.add(numLayers);
+ 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);
+ }
+ }
+ }
+}
diff --git a/service/src/com/android/car/storagemonitoring/EMmcWearInformationProvider.java b/service/src/com/android/car/storagemonitoring/EMmcWearInformationProvider.java
new file mode 100644
index 0000000..3940578
--- /dev/null
+++ b/service/src/com/android/car/storagemonitoring/EMmcWearInformationProvider.java
@@ -0,0 +1,106 @@
+/*
+ * 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.storagemonitoring;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
+import com.android.car.CarLog;
+import com.android.internal.annotations.VisibleForTesting;
+import java.io.File;
+import java.io.FileReader;
+import java.io.BufferedReader;
+import java.io.IOException;
+
+/**
+ * Loads wear information from the eMMC sysfs entry points.
+ * sysfs exposes eMMC lifetime data in /sys/bus/mmc/devices/mmc0:0001/{pre_eol_info|life_time}
+ * The first file contains the value of the PRE_EOL_INFO register as an hexadecimal string
+ * The second file contains the values of the LIFE_TIME_EST A and B registers as two hexadecimal
+ * strings separated by a space.
+ */
+public class EMmcWearInformationProvider implements WearInformationProvider {
+ private static File DEFAULT_LIFE_TIME_FILE =
+ new File("/sys/bus/mmc/devices/mmc0:0001/life_time");
+
+ private static File DEFAULT_PRE_EOL_FILE =
+ new File("/sys/bus/mmc/devices/mmc0:0001/pre_eol_info");
+
+ private File mLifetimeFile;
+ private File mPreEolFile;
+
+ public EMmcWearInformationProvider() {
+ this(DEFAULT_LIFE_TIME_FILE, DEFAULT_PRE_EOL_FILE);
+ }
+
+ @VisibleForTesting
+ EMmcWearInformationProvider(@NonNull File lifetimeFile, @NonNull File preEolFile) {
+ mLifetimeFile = lifetimeFile;
+ mPreEolFile = preEolFile;
+ }
+
+ private String readLineFromFile(File f) {
+ if (!f.exists() || !f.isFile()) {
+ Log.i(CarLog.TAG_STORAGE, f + " does not exist or is not a file");
+ return null;
+ }
+
+ try {
+ BufferedReader reader = new BufferedReader(new FileReader(f));
+ String data = reader.readLine();
+ reader.close();
+ return data;
+ } catch (IOException e) {
+ Log.w(CarLog.TAG_STORAGE, f + " cannot be read from", e);
+ return null;
+ }
+ }
+
+ @Nullable
+ @Override
+ public WearInformation load() {
+ String lifetimeData = readLineFromFile(mLifetimeFile);
+ String eolData = readLineFromFile(mPreEolFile);
+
+ if ((lifetimeData == null) || (eolData == null)) {
+ return null;
+ }
+
+ String[] lifetimes = lifetimeData.split(" ");
+ if (lifetimes.length != 2) {
+ Log.w(CarLog.TAG_STORAGE, "lifetime data not in expected format: " + lifetimeData);
+ return null;
+ }
+
+ int lifetimeA;
+ int lifetimeB;
+ int eol;
+
+ try {
+ lifetimeA = Integer.decode(lifetimes[0]);
+ lifetimeB = Integer.decode(lifetimes[1]);
+ eol = Integer.decode("0x" + eolData);
+ } catch (NumberFormatException e) {
+ Log.w(CarLog.TAG_STORAGE,
+ "lifetime data not in expected format: " + lifetimeData, e);
+ return null;
+ }
+
+ return new WearInformation(convertLifetime(lifetimeA),
+ convertLifetime(lifetimeB),
+ adjustEol(eol));
+ }
+}
diff --git a/service/src/com/android/car/storagemonitoring/IoStatsTracker.java b/service/src/com/android/car/storagemonitoring/IoStatsTracker.java
new file mode 100644
index 0000000..25a3d69
--- /dev/null
+++ b/service/src/com/android/car/storagemonitoring/IoStatsTracker.java
@@ -0,0 +1,118 @@
+/*
+ * 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.storagemonitoring;
+
+import android.car.storagemonitoring.UidIoStats;
+import android.car.storagemonitoring.UidIoStatsRecord;
+import android.util.SparseArray;
+import com.android.car.SparseArrayStream;
+import com.android.car.procfsinspector.ProcessInfo;
+import com.android.car.systeminterface.SystemStateInterface;
+import java.util.List;
+import java.util.Optional;
+
+public class IoStatsTracker {
+ private abstract class Lazy<T> {
+ protected Optional<T> mLazy = Optional.empty();
+
+ protected abstract T supply();
+
+ public synchronized T get() {
+ if (!mLazy.isPresent()) {
+ mLazy = Optional.of(supply());
+ }
+ return mLazy.get();
+ }
+ }
+
+ private final long mSampleWindowMs;
+ private final SystemStateInterface mSystemStateInterface;
+ private SparseArray<UidIoStats> mTotal;
+ private SparseArray<UidIoStats> mCurrentSample;
+
+ public IoStatsTracker(List<UidIoStats> initialValue,
+ long sampleWindowMs, SystemStateInterface systemStateInterface) {
+ mTotal = new SparseArray<>(initialValue.size());
+ initialValue.forEach(uidIoStats -> mTotal.append(uidIoStats.uid, uidIoStats));
+ mCurrentSample = mTotal.clone();
+ mSampleWindowMs = sampleWindowMs;
+ mSystemStateInterface = systemStateInterface;
+ }
+
+ public synchronized void update(SparseArray<UidIoStatsRecord> newMetrics) {
+ final Lazy<List<ProcessInfo>> processTable = new Lazy<List<ProcessInfo>>() {
+ @Override
+ protected List<ProcessInfo> supply() {
+ return mSystemStateInterface.getRunningProcesses();
+ }
+ };
+
+ SparseArray<UidIoStats> newSample = new SparseArray<>();
+ SparseArray<UidIoStats> newTotal = new SparseArray<>();
+
+ // prepare the new values
+ SparseArrayStream.valueStream(newMetrics).forEach( newRecord -> {
+ final int uid = newRecord.uid;
+ final UidIoStats oldRecord = mTotal.get(uid);
+
+ UidIoStats newStats = null;
+
+ if (oldRecord == null) {
+ // this user id has just showed up, so just add it to the current sample
+ // and its runtime is the size of our sample window
+ newStats = new UidIoStats(newRecord, mSampleWindowMs);
+ } else {
+ // this user id has already been detected
+
+ if (oldRecord.representsSameMetrics(newRecord)) {
+ // if no new I/O happened, try to figure out if any process on behalf
+ // of this user has happened, and use that to update the runtime metrics
+ if (processTable.get().stream().anyMatch(pi -> pi.uid == uid)) {
+ newStats = new UidIoStats(newRecord.delta(oldRecord),
+ oldRecord.runtimeMillis + mSampleWindowMs);
+ }
+ // if no new I/O happened and no process is running for this user
+ // then do not prepare a new sample, as nothing has changed
+ } else {
+ // but if new I/O happened, assume something was running for the entire
+ // sample window and compute the delta
+ newStats = new UidIoStats(newRecord.delta(oldRecord),
+ oldRecord.runtimeMillis + mSampleWindowMs);
+ }
+ }
+
+ if (newStats != null) {
+ newSample.put(uid, newStats);
+ newTotal.append(uid, new UidIoStats(newRecord, newStats.runtimeMillis));
+ } else {
+ // if oldRecord were null, newStats would be != null and we wouldn't be here
+ newTotal.append(uid, oldRecord);
+ }
+ });
+
+ // now update the stored values
+ mCurrentSample = newSample;
+ mTotal = newTotal;
+ }
+
+ public synchronized SparseArray<UidIoStats> getTotal() {
+ return mTotal;
+ }
+
+ public synchronized SparseArray<UidIoStats> getCurrentSample() {
+ return mCurrentSample;
+ }
+}
diff --git a/service/src/com/android/car/storagemonitoring/ProcfsUidIoStatsProvider.java b/service/src/com/android/car/storagemonitoring/ProcfsUidIoStatsProvider.java
new file mode 100644
index 0000000..73b0da6
--- /dev/null
+++ b/service/src/com/android/car/storagemonitoring/ProcfsUidIoStatsProvider.java
@@ -0,0 +1,105 @@
+/*
+ * 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.storagemonitoring;
+
+import android.annotation.Nullable;
+import android.car.storagemonitoring.UidIoStatsRecord;
+import android.util.Log;
+import android.util.SparseArray;
+import com.android.car.CarLog;
+import com.android.internal.annotations.VisibleForTesting;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Objects;
+import java.util.StringTokenizer;
+
+/**
+ * Loads I/O stats from procfs
+ *
+ * The Android kernel can be configured to provide uid I/O stats in /proc/uid_io/stats.
+ */
+public class ProcfsUidIoStatsProvider implements UidIoStatsProvider {
+ private static Path DEFAULT_STATS_FILE = new File("/proc/uid_io/stats").toPath();
+
+ private final Path mStatsFile;
+
+ public ProcfsUidIoStatsProvider() {
+ this(DEFAULT_STATS_FILE);
+ }
+
+ @VisibleForTesting
+ ProcfsUidIoStatsProvider(Path statsFile) {
+ mStatsFile = Objects.requireNonNull(statsFile);
+ }
+
+ @Nullable
+ @Override
+ public SparseArray<UidIoStatsRecord> load() {
+ List<String> lines;
+ SparseArray<UidIoStatsRecord> result = new SparseArray<>();
+ try {
+ lines = Files.readAllLines(mStatsFile);
+ } catch (IOException e) {
+ Log.w(CarLog.TAG_STORAGE, "can't read I/O stats from " + mStatsFile, e);
+ return null;
+ }
+
+ for (String line : lines) {
+ StringTokenizer tokenizer = new StringTokenizer(line);
+ if (tokenizer.countTokens() != 11) {
+ Log.w(CarLog.TAG_STORAGE, "malformed I/O stats entry: " + line);
+ return null;
+ }
+
+ try {
+ int uid = Integer.valueOf(tokenizer.nextToken());
+ long foreground_rchar = Long.valueOf(tokenizer.nextToken());
+ long foreground_wchar = Long.valueOf(tokenizer.nextToken());
+ long foreground_read_bytes = Long.valueOf(tokenizer.nextToken());
+ long foreground_write_bytes = Long.valueOf(tokenizer.nextToken());
+ long background_rchar = Long.valueOf(tokenizer.nextToken());
+ long background_wchar = Long.valueOf(tokenizer.nextToken());
+ long background_read_bytes = Long.valueOf(tokenizer.nextToken());
+ long background_write_bytes = Long.valueOf(tokenizer.nextToken());
+ long foreground_fsync = Long.valueOf(tokenizer.nextToken());
+ long background_fsync = Long.valueOf(tokenizer.nextToken());
+
+ result.append(uid, new UidIoStatsRecord(uid,
+ foreground_rchar,
+ foreground_wchar,
+ foreground_read_bytes,
+ foreground_write_bytes,
+ foreground_fsync,
+ background_rchar,
+ background_wchar,
+ background_read_bytes,
+ background_write_bytes,
+ background_fsync));
+
+ } catch (NumberFormatException e) {
+ Log.w(CarLog.TAG_STORAGE, "malformed I/O stats entry: " + line, e);
+ return null;
+ }
+ }
+
+ return result;
+ }
+}
+
+
diff --git a/service/src/com/android/car/storagemonitoring/UfsWearInformationProvider.java b/service/src/com/android/car/storagemonitoring/UfsWearInformationProvider.java
new file mode 100644
index 0000000..fa7ba8b
--- /dev/null
+++ b/service/src/com/android/car/storagemonitoring/UfsWearInformationProvider.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 com.android.car.storagemonitoring;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
+import com.android.car.CarLog;
+import com.android.internal.annotations.VisibleForTesting;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+import java.util.Scanner;
+import java.util.regex.MatchResult;
+import java.util.regex.Pattern;
+
+/**
+ * Loads wear information from the UFS sysfs entry points.
+ * sysfs exposes UFS lifetime data in /sys/devices/soc/624000.ufshc/health
+ * The first line of the file contains the UFS version
+ * Subsequent lines contains individual information points in the format:
+ * Health Descriptor[Byte offset 0x%d]: %31s = 0x%hx
+ * Of these we care about the key values bPreEOLInfo and bDeviceLifeTimeEstA bDeviceLifeTimeEstB
+ */
+public class UfsWearInformationProvider implements WearInformationProvider {
+ private static File DEFAULT_FILE =
+ new File("/sys/devices/soc/624000.ufshc/health");
+
+ private File mFile;
+
+ public UfsWearInformationProvider() {
+ this(DEFAULT_FILE);
+ }
+
+ @VisibleForTesting
+ public UfsWearInformationProvider(@NonNull File file) {
+ mFile = file;
+ }
+
+ @Nullable
+ @Override
+ public WearInformation load() {
+ List<String> lifetimeData;
+ try {
+ lifetimeData = java.nio.file.Files.readAllLines(mFile.toPath());
+ } catch (IOException e) {
+ Log.w(CarLog.TAG_STORAGE, "error reading " + mFile, e);
+ return null;
+ }
+ if (lifetimeData == null || lifetimeData.size() < 4) {
+ return null;
+ }
+
+ Pattern infoPattern = Pattern.compile(
+ "Health Descriptor\\[Byte offset 0x\\d+\\]: (\\w+) = 0x([0-9a-fA-F]+)");
+
+ Optional<Integer> lifetimeA = Optional.empty();
+ Optional<Integer> lifetimeB = Optional.empty();
+ Optional<Integer> eol = Optional.empty();
+
+ for(String lifetimeInfo : lifetimeData) {
+ Scanner scanner = new Scanner(lifetimeInfo);
+ if (null == scanner.findInLine(infoPattern)) {
+ continue;
+ }
+ MatchResult match = scanner.match();
+ if (match.groupCount() != 2) {
+ continue;
+ }
+ String name = match.group(1);
+ String value = "0x" + match.group(2);
+ try {
+ switch (name) {
+ case "bPreEOLInfo":
+ eol = Optional.of(Integer.decode(value));
+ break;
+ case "bDeviceLifeTimeEstA":
+ lifetimeA = Optional.of(Integer.decode(value));
+ break;
+ case "bDeviceLifeTimeEstB":
+ lifetimeB = Optional.of(Integer.decode(value));
+ break;
+ }
+ } catch (NumberFormatException e) {
+ Log.w(CarLog.TAG_STORAGE,
+ "trying to decode key " + name + " value " + value + " didn't parse properly", e);
+ }
+ }
+
+ if (!lifetimeA.isPresent() || !lifetimeB.isPresent() || !eol.isPresent()) {
+ return null;
+ }
+
+ return new WearInformation(convertLifetime(lifetimeA.get()),
+ convertLifetime(lifetimeB.get()),
+ adjustEol(eol.get()));
+ }
+}
diff --git a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl b/service/src/com/android/car/storagemonitoring/UidIoStatsProvider.java
similarity index 63%
copy from libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
copy to service/src/com/android/car/storagemonitoring/UidIoStatsProvider.java
index ca0ac83..21798c2 100644
--- a/libvehiclemonitor/java/src/com/android/car/vehiclemonitor/IVehicleMonitorListener.aidl
+++ b/service/src/com/android/car/storagemonitoring/UidIoStatsProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.car.storagemonitoring;
-package com.android.car.vehiclemonitor;
+import android.annotation.Nullable;
+import android.car.storagemonitoring.UidIoStatsRecord;
+import android.util.SparseArray;
-/**
- * Listener for vehicle monitor service.
- * @hide
- */
-oneway interface IVehicleMonitorListener {
- void onAppViolation(int pid, int uid, int action, int violation) = 0;
+public interface UidIoStatsProvider {
+ @Nullable
+ SparseArray<UidIoStatsRecord> load();
}
diff --git a/service/src/com/android/car/storagemonitoring/WearEstimateRecord.java b/service/src/com/android/car/storagemonitoring/WearEstimateRecord.java
new file mode 100644
index 0000000..fd4aae7
--- /dev/null
+++ b/service/src/com/android/car/storagemonitoring/WearEstimateRecord.java
@@ -0,0 +1,178 @@
+/*
+ * 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.storagemonitoring;
+
+import android.annotation.NonNull;
+import android.car.storagemonitoring.WearEstimate;
+import android.car.storagemonitoring.WearEstimateChange;
+import android.util.JsonWriter;
+import java.io.IOException;
+import java.time.Instant;
+import java.util.Objects;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * This class represents a wear estimate record as stored by CarStorageMonitoringService.
+ *
+ * Because it is meant to map 1:1 to on-disk records, it is not directly convertible to a
+ * WearEstimateChange because it does not include information about "acceptable degradation".
+ */
+public class WearEstimateRecord {
+
+ private final WearEstimate mOldWearEstimate;
+ private final WearEstimate mNewWearEstimate;
+ private final long mTotalCarServiceUptime;
+ private final Instant mUnixTimestamp;
+
+ public WearEstimateRecord(@NonNull WearEstimate oldWearEstimate,
+ @NonNull WearEstimate newWearEstimate,
+ long totalCarServiceUptime,
+ @NonNull Instant unixTimestamp) {
+ mOldWearEstimate = Objects.requireNonNull(oldWearEstimate);
+ mNewWearEstimate = Objects.requireNonNull(newWearEstimate);
+ mTotalCarServiceUptime = totalCarServiceUptime;
+ mUnixTimestamp = Objects.requireNonNull(unixTimestamp);
+ }
+
+ WearEstimateRecord(@NonNull JSONObject json) throws JSONException {
+ mOldWearEstimate = new WearEstimate(json.getJSONObject("oldWearEstimate"));
+ mNewWearEstimate = new WearEstimate(json.getJSONObject("newWearEstimate"));
+ mTotalCarServiceUptime = json.getLong("totalCarServiceUptime");
+ mUnixTimestamp = Instant.ofEpochMilli(json.getLong("unixTimestamp"));
+
+ }
+
+ void writeToJson(@NonNull JsonWriter jsonWriter) throws IOException {
+ jsonWriter.beginObject();
+ jsonWriter.name("oldWearEstimate"); mOldWearEstimate.writeToJson(jsonWriter);
+ jsonWriter.name("newWearEstimate"); mNewWearEstimate.writeToJson(jsonWriter);
+ jsonWriter.name("totalCarServiceUptime").value(mTotalCarServiceUptime);
+ jsonWriter.name("unixTimestamp").value(mUnixTimestamp.toEpochMilli());
+ jsonWriter.endObject();
+ }
+
+ public WearEstimate getOldWearEstimate() {
+ return mOldWearEstimate;
+ }
+
+ public WearEstimate getNewWearEstimate() {
+ return mNewWearEstimate;
+ }
+
+ public long getTotalCarServiceUptime() {
+ return mTotalCarServiceUptime;
+ }
+
+ public Instant getUnixTimestamp() {
+ return mUnixTimestamp;
+ }
+
+ WearEstimateChange toWearEstimateChange(boolean isAcceptableDegradation) {
+ return new WearEstimateChange(mOldWearEstimate,
+ mNewWearEstimate, mTotalCarServiceUptime, mUnixTimestamp, isAcceptableDegradation);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof WearEstimateRecord) {
+ WearEstimateRecord wer = (WearEstimateRecord)other;
+ if (!wer.mOldWearEstimate.equals(mOldWearEstimate)) return false;
+ if (!wer.mNewWearEstimate.equals(mNewWearEstimate)) return false;
+ if (wer.mTotalCarServiceUptime != mTotalCarServiceUptime) return false;
+ if (!wer.mUnixTimestamp.equals(mUnixTimestamp)) return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Checks whether this record tracks the same change as the provided estimate.
+ * That means the two objects have the same values for:
+ * <ul>
+ * <li>old wear indicators</li>
+ * <li>new wear indicators</li>
+ * <li>uptime at event</li>
+ * </ul>
+ */
+ public boolean isSameAs(@NonNull WearEstimateChange wearEstimateChange) {
+ if (!mOldWearEstimate.equals(wearEstimateChange.oldEstimate)) return false;
+ if (!mNewWearEstimate.equals(wearEstimateChange.newEstimate)) return false;
+ return (mTotalCarServiceUptime == wearEstimateChange.uptimeAtChange);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mOldWearEstimate,
+ mNewWearEstimate, mTotalCarServiceUptime, mUnixTimestamp);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("WearEstimateRecord {," +
+ "mOldWearEstimate = %s, " +
+ "mNewWearEstimate = %s, " +
+ "mTotalCarServiceUptime = %d, " +
+ "mUnixTimestamp = %s}",
+ mOldWearEstimate, mNewWearEstimate, mTotalCarServiceUptime, mUnixTimestamp);
+ }
+
+ public static final class Builder {
+ private WearEstimate mOldWearEstimate = null;
+ private WearEstimate mNewWearEstimate = null;
+ private long mTotalCarServiceUptime = -1;
+ private Instant mUnixTimestamp = null;
+
+ private Builder() {}
+
+ public static Builder newBuilder() {
+ return new Builder();
+ }
+
+ public Builder fromWearEstimate(@NonNull WearEstimate wearEstimate) {
+ mOldWearEstimate = Objects.requireNonNull(wearEstimate);
+ return this;
+ }
+
+ public Builder toWearEstimate(@NonNull WearEstimate wearEstimate) {
+ mNewWearEstimate = Objects.requireNonNull(wearEstimate);
+ return this;
+ }
+
+ public Builder atUptime(long uptime) {
+ if (uptime < 0) {
+ throw new IllegalArgumentException("uptime must be >= 0");
+ }
+ mTotalCarServiceUptime = uptime;
+ return this;
+ }
+
+ public Builder atTimestamp(@NonNull Instant now) {
+ mUnixTimestamp = Objects.requireNonNull(now);
+ return this;
+ }
+
+ public WearEstimateRecord build() {
+ if (mOldWearEstimate == null || mNewWearEstimate == null ||
+ mTotalCarServiceUptime < 0 || mUnixTimestamp == null) {
+ throw new IllegalStateException("malformed builder state");
+ }
+ return new WearEstimateRecord(
+ mOldWearEstimate, mNewWearEstimate, mTotalCarServiceUptime, mUnixTimestamp);
+ }
+ }
+}
diff --git a/service/src/com/android/car/storagemonitoring/WearHistory.java b/service/src/com/android/car/storagemonitoring/WearHistory.java
new file mode 100644
index 0000000..38982aa
--- /dev/null
+++ b/service/src/com/android/car/storagemonitoring/WearHistory.java
@@ -0,0 +1,147 @@
+/*
+ * 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.storagemonitoring;
+
+import android.annotation.NonNull;
+import android.car.storagemonitoring.WearEstimateChange;
+import android.util.JsonWriter;
+import com.android.car.CarStorageMonitoringService;
+import com.android.car.R;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.BiPredicate;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * This class represents the entire history of flash wear changes as tracked
+ * by CarStorageMonitoringService. It is a set of WearEstimateRecords.
+ *
+ * This is convertible to a list of WearEstimateChanges given policy about what constitutes
+ * acceptable or not acceptable degradation across change events. This policy is subject to
+ * modifications across OS versions, and as such it is not suitable for permanent storage.
+ */
+public class WearHistory {
+ private final List<WearEstimateRecord> mWearHistory = new ArrayList<>();
+
+ public WearHistory() {}
+
+ WearHistory(@NonNull JSONObject jsonObject) throws JSONException {
+ final JSONArray wearHistory = jsonObject.getJSONArray("wearHistory");
+ for (int i = 0; i < wearHistory.length(); ++i) {
+ JSONObject wearRecordJson = wearHistory.getJSONObject(i);
+ WearEstimateRecord wearRecord = new WearEstimateRecord(wearRecordJson);
+ add(wearRecord);
+ }
+ }
+
+ public static WearHistory fromRecords(@NonNull WearEstimateRecord... records) {
+ WearHistory wearHistory = new WearHistory();
+ Arrays.stream(records).forEach(wearHistory::add);
+ return wearHistory;
+ }
+
+ public static WearHistory fromJson(@NonNull File in) throws IOException, JSONException {
+ JSONObject jsonObject = new JSONObject(new String(Files.readAllBytes(in.toPath())));
+ return new WearHistory(jsonObject);
+ }
+
+ public void writeToJson(@NonNull JsonWriter out) throws IOException {
+ out.beginObject();
+ out.name("wearHistory").beginArray();
+ for (WearEstimateRecord wearRecord : mWearHistory) {
+ wearRecord.writeToJson(out);
+ }
+ out.endArray();
+ out.endObject();
+ }
+
+ public boolean add(@NonNull WearEstimateRecord record) {
+ if (record != null && mWearHistory.add(record)) {
+ mWearHistory.sort((WearEstimateRecord o1, WearEstimateRecord o2) ->
+ Long.valueOf(o1.getTotalCarServiceUptime()).compareTo(
+ o2.getTotalCarServiceUptime()));
+ return true;
+ }
+ return false;
+ }
+
+ public int size() {
+ return mWearHistory.size();
+ }
+
+ public WearEstimateRecord get(int i) {
+ return mWearHistory.get(i);
+ }
+
+ public WearEstimateRecord getLast() {
+ return get(size() - 1);
+ }
+
+ public List<WearEstimateChange> toWearEstimateChanges(
+ long acceptableHoursPerOnePercentFlashWear) {
+ // current technology allows us to detect wear in 10% increments
+ final int WEAR_PERCENTAGE_INCREMENT = 10;
+ final long acceptableWearRate = WEAR_PERCENTAGE_INCREMENT *
+ Duration.ofHours(acceptableHoursPerOnePercentFlashWear).toMillis();
+ final int numRecords = size();
+
+ if (numRecords == 0) return Collections.emptyList();
+
+ List<WearEstimateChange> result = new ArrayList<>();
+ result.add(get(0).toWearEstimateChange(true));
+
+ for (int i = 1; i < numRecords; ++i) {
+ WearEstimateRecord previousRecord = get(i - 1);
+ WearEstimateRecord currentRecord = get(i);
+ final long timeForChange =
+ currentRecord.getTotalCarServiceUptime() -
+ previousRecord.getTotalCarServiceUptime();
+ final boolean isAcceptableDegradation = timeForChange >= acceptableWearRate;
+ result.add(currentRecord.toWearEstimateChange(isAcceptableDegradation));
+ }
+
+ return Collections.unmodifiableList(result);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof WearHistory) {
+ WearHistory wi = (WearHistory)other;
+ return wi.mWearHistory.equals(mWearHistory);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return mWearHistory.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return mWearHistory.stream().map(WearEstimateRecord::toString).reduce(
+ "WearHistory[size = " + size() + "] -> ",
+ (String s, String t) -> s + ", " + t);
+ }
+}
diff --git a/service/src/com/android/car/storagemonitoring/WearInformation.java b/service/src/com/android/car/storagemonitoring/WearInformation.java
new file mode 100644
index 0000000..da38afe
--- /dev/null
+++ b/service/src/com/android/car/storagemonitoring/WearInformation.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 com.android.car.storagemonitoring;
+
+import static java.util.Objects.hash;
+
+import android.car.storagemonitoring.WearEstimate;
+import java.time.Instant;
+
+public final class WearInformation {
+ public static final int UNKNOWN_LIFETIME_ESTIMATE = -1;
+
+ public static final int UNKNOWN_PRE_EOL_INFO = 0;
+ public static final int PRE_EOL_INFO_NORMAL = 1;
+ public static final int PRE_EOL_INFO_WARNING = 2;
+ public static final int PRE_EOL_INFO_URGENT = 3;
+
+ private static final String UNKNOWN = "unknown";
+ private static final String[] PRE_EOL_STRINGS = new String[] {
+ UNKNOWN, "normal", "warning", "urgent"
+ };
+
+ /**
+ * A lower bound on the lifetime estimate for "type A" memory cells, expressed as a percentage.
+ */
+ public final int lifetimeEstimateA;
+
+ /**
+ * A lower bound on the lifetime estimate for "type B" memory cells, expressed as a percentage.
+ */
+ public final int lifetimeEstimateB;
+
+ /**
+ * An estimate of the lifetime based on reserved block consumption.
+ */
+ public final int preEolInfo;
+
+ public WearInformation(int lifetimeA, int lifetimeB, int preEol) {
+ lifetimeEstimateA = lifetimeA;
+ lifetimeEstimateB = lifetimeB;
+ preEolInfo = preEol;
+ }
+
+ @Override
+ public int hashCode() {
+ return hash(lifetimeEstimateA, lifetimeEstimateB, preEolInfo);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof WearInformation) {
+ WearInformation wi = (WearInformation)other;
+ return (wi.lifetimeEstimateA == lifetimeEstimateA) &&
+ (wi.lifetimeEstimateB == lifetimeEstimateB) &&
+ (wi.preEolInfo == preEolInfo);
+ } else {
+ return false;
+ }
+ }
+
+ private String lifetimeToString(int lifetime) {
+ if (lifetime == UNKNOWN_LIFETIME_ESTIMATE) return UNKNOWN;
+
+ return lifetime + "%";
+ }
+
+ @Override
+ public String toString() {
+ return String.format("lifetime estimate: A = %s, B = %s; pre EOL info: %s",
+ lifetimeToString(lifetimeEstimateA),
+ lifetimeToString(lifetimeEstimateB),
+ PRE_EOL_STRINGS[preEolInfo]);
+ }
+
+ public WearEstimate toWearEstimate() {
+ return new WearEstimate(lifetimeEstimateA, lifetimeEstimateB);
+ }
+}
diff --git a/service/src/com/android/car/storagemonitoring/WearInformationProvider.java b/service/src/com/android/car/storagemonitoring/WearInformationProvider.java
new file mode 100644
index 0000000..65574d7
--- /dev/null
+++ b/service/src/com/android/car/storagemonitoring/WearInformationProvider.java
@@ -0,0 +1,34 @@
+/*
+ * 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.storagemonitoring;
+
+import android.annotation.Nullable;
+
+public interface WearInformationProvider {
+ @Nullable
+ WearInformation load();
+
+ default int convertLifetime(int lifetime) {
+ if ((lifetime <= 0) || (lifetime > 11)) return WearInformation.UNKNOWN_LIFETIME_ESTIMATE;
+ return 10 * (lifetime - 1);
+ }
+
+ default int adjustEol(int eol) {
+ if ((eol <= 0) || (eol > 3)) return WearInformation.UNKNOWN_PRE_EOL_INFO;
+ return eol;
+ }
+
+}
diff --git a/service/src/com/android/car/systeminterface/DisplayInterface.java b/service/src/com/android/car/systeminterface/DisplayInterface.java
new file mode 100644
index 0000000..f798b0d
--- /dev/null
+++ b/service/src/com/android/car/systeminterface/DisplayInterface.java
@@ -0,0 +1,115 @@
+/*
+ * 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.android.car.systeminterface;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.Display;
+import com.android.car.CarLog;
+import com.android.car.CarPowerManagementService;
+
+/**
+ * Interface that abstracts display operations
+ */
+public interface DisplayInterface {
+ void setDisplayState(boolean on);
+ void startDisplayStateMonitoring(CarPowerManagementService service);
+ void stopDisplayStateMonitoring();
+
+ class DefaultImpl implements DisplayInterface {
+ private final DisplayManager mDisplayManager;
+ private final PowerManager mPowerManager;
+ private final WakeLockInterface mWakeLockInterface;
+ private CarPowerManagementService mService;
+ private boolean mDisplayStateSet;
+ private final DisplayManager.DisplayListener mDisplayListener = new DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ //ignore
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ //ignore
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ handleMainDisplayChanged();
+ }
+ }
+ };
+
+ DefaultImpl(Context context, WakeLockInterface wakeLockInterface) {
+ mWakeLockInterface = wakeLockInterface;
+ mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+ mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ }
+
+ private void handleMainDisplayChanged() {
+ boolean isOn = isMainDisplayOn();
+ CarPowerManagementService service;
+ synchronized (this) {
+ if (mDisplayStateSet == isOn) { // same as what is set
+ return;
+ }
+ service = mService;
+ }
+ service.handleMainDisplayChanged(isOn);
+ }
+
+ private boolean isMainDisplayOn() {
+ Display disp = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ return disp.getState() == Display.STATE_ON;
+ }
+
+ @Override
+ public void startDisplayStateMonitoring(CarPowerManagementService service) {
+ synchronized (this) {
+ mService = service;
+ mDisplayStateSet = isMainDisplayOn();
+ }
+ mDisplayManager.registerDisplayListener(mDisplayListener, service.getHandler());
+ }
+
+ @Override
+ public void stopDisplayStateMonitoring() {
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ }
+
+ @Override
+ public void setDisplayState(boolean on) {
+ synchronized (this) {
+ mDisplayStateSet = on;
+ }
+ if (on) {
+ mWakeLockInterface.switchToFullWakeLock();
+ Log.i(CarLog.TAG_POWER, "on display");
+ mPowerManager.wakeUp(SystemClock.uptimeMillis());
+ } else {
+ mWakeLockInterface.switchToPartialWakeLock();
+ Log.i(CarLog.TAG_POWER, "off display");
+ mPowerManager.goToSleep(SystemClock.uptimeMillis());
+ }
+ }
+ }
+}
diff --git a/service/src/com/android/car/systeminterface/IOInterface.java b/service/src/com/android/car/systeminterface/IOInterface.java
new file mode 100644
index 0000000..2ce9174
--- /dev/null
+++ b/service/src/com/android/car/systeminterface/IOInterface.java
@@ -0,0 +1,40 @@
+/*
+ * 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.android.car.systeminterface;
+
+import android.content.Context;
+import java.io.File;
+
+/**
+ * Interface that abstracts I/O operations
+ */
+public interface IOInterface {
+ File getFilesDir();
+
+ class DefaultImpl implements IOInterface {
+ private final File mFilesDir;
+
+ DefaultImpl(Context context) {
+ mFilesDir = context.getFilesDir();
+ }
+
+ @Override
+ public File getFilesDir() {
+ return mFilesDir;
+ }
+ }
+}
diff --git a/service/src/com/android/car/systeminterface/StorageMonitoringInterface.java b/service/src/com/android/car/systeminterface/StorageMonitoringInterface.java
new file mode 100644
index 0000000..5825f8f
--- /dev/null
+++ b/service/src/com/android/car/systeminterface/StorageMonitoringInterface.java
@@ -0,0 +1,41 @@
+/*
+ * 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.android.car.systeminterface;
+
+import com.android.car.storagemonitoring.EMmcWearInformationProvider;
+import com.android.car.storagemonitoring.ProcfsUidIoStatsProvider;
+import com.android.car.storagemonitoring.UfsWearInformationProvider;
+import com.android.car.storagemonitoring.UidIoStatsProvider;
+import com.android.car.storagemonitoring.WearInformationProvider;
+
+/**
+ * Interface that abstracts storage monitoring operations
+ */
+public interface StorageMonitoringInterface {
+ default WearInformationProvider[] getFlashWearInformationProviders() {
+ return new WearInformationProvider[] {
+ new EMmcWearInformationProvider(),
+ new UfsWearInformationProvider()
+ };
+ }
+
+ default UidIoStatsProvider getUidIoStatsProvider() {
+ return new ProcfsUidIoStatsProvider();
+ }
+
+ class DefaultImpl implements StorageMonitoringInterface {}
+}
diff --git a/service/src/com/android/car/systeminterface/SystemInterface.java b/service/src/com/android/car/systeminterface/SystemInterface.java
new file mode 100644
index 0000000..1e984f9
--- /dev/null
+++ b/service/src/com/android/car/systeminterface/SystemInterface.java
@@ -0,0 +1,234 @@
+/*
+ * 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.android.car.systeminterface;
+
+import android.content.Context;
+import com.android.car.CarPowerManagementService;
+import com.android.car.procfsinspector.ProcessInfo;
+import com.android.car.storagemonitoring.UidIoStatsProvider;
+import com.android.car.storagemonitoring.WearInformationProvider;
+import java.io.File;
+import java.time.Duration;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * This class contains references to all the different wrapper interfaces between
+ * CarService and the Android OS APIs.
+ */
+public final class SystemInterface implements DisplayInterface, IOInterface,
+ StorageMonitoringInterface, SystemStateInterface, TimeInterface,
+ WakeLockInterface {
+ private final DisplayInterface mDisplayInterface;
+ private final IOInterface mIOInterface;
+ private final StorageMonitoringInterface mStorageMonitoringInterface;
+ private final SystemStateInterface mSystemStateInterface;
+ private final TimeInterface mTimeInterface;
+ private final WakeLockInterface mWakeLockInterface;
+
+ SystemInterface(DisplayInterface displayInterface,
+ IOInterface ioInterface,
+ StorageMonitoringInterface storageMonitoringInterface,
+ SystemStateInterface systemStateInterface,
+ TimeInterface timeInterface,
+ WakeLockInterface wakeLockInterface) {
+ mDisplayInterface = displayInterface;
+ mIOInterface = ioInterface;
+ mStorageMonitoringInterface = storageMonitoringInterface;
+ mSystemStateInterface = systemStateInterface;
+ mTimeInterface = timeInterface;
+ mWakeLockInterface = wakeLockInterface;
+ }
+
+ public DisplayInterface getDisplayInterface() { return mDisplayInterface; }
+ public IOInterface getIOInterface() { return mIOInterface; }
+ public SystemStateInterface getSystemStateInterface() { return mSystemStateInterface; }
+ public TimeInterface getTimeInterface() { return mTimeInterface; }
+ public WakeLockInterface getWakeLockInterface() { return mWakeLockInterface; }
+
+ @Override
+ public File getFilesDir() {
+ return mIOInterface.getFilesDir();
+ }
+
+ @Override
+ public void releaseAllWakeLocks() {
+ mWakeLockInterface.releaseAllWakeLocks();
+ }
+
+ @Override
+ public void switchToPartialWakeLock() {
+ mWakeLockInterface.switchToPartialWakeLock();
+ }
+
+ @Override
+ public void switchToFullWakeLock() {
+ mWakeLockInterface.switchToFullWakeLock();
+ }
+
+ @Override
+ public long getUptime() {
+ return mTimeInterface.getUptime();
+ }
+
+ @Override
+ public long getUptime(boolean includeDeepSleepTime) {
+ return mTimeInterface.getUptime(includeDeepSleepTime);
+ }
+
+ @Override
+ public void scheduleAction(Runnable r, long delayMs) {
+ mTimeInterface.scheduleAction(r, delayMs);
+ }
+
+ @Override
+ public List<ProcessInfo> getRunningProcesses() {
+ return mSystemStateInterface.getRunningProcesses();
+ }
+
+ @Override
+ public void cancelAllActions() {
+ mTimeInterface.cancelAllActions();
+ }
+
+ @Override
+ public void setDisplayState(boolean on) {
+ mDisplayInterface.setDisplayState(on);
+ }
+
+ @Override
+ public void startDisplayStateMonitoring(CarPowerManagementService service) {
+ mDisplayInterface.startDisplayStateMonitoring(service);
+ }
+
+ @Override
+ public void stopDisplayStateMonitoring() {
+ mDisplayInterface.stopDisplayStateMonitoring();
+ }
+
+ @Override
+ public WearInformationProvider[] getFlashWearInformationProviders() {
+ return mStorageMonitoringInterface.getFlashWearInformationProviders();
+ }
+
+ @Override
+ public UidIoStatsProvider getUidIoStatsProvider() {
+ return mStorageMonitoringInterface.getUidIoStatsProvider();
+ }
+
+ @Override
+ public void shutdown() {
+ mSystemStateInterface.shutdown();
+ }
+
+ @Override
+ public void enterDeepSleep(int wakeupTimeSec) {
+ mSystemStateInterface.enterDeepSleep(wakeupTimeSec);
+ }
+
+ @Override
+ public void scheduleActionForBootCompleted(Runnable action, Duration delay) {
+ mSystemStateInterface.scheduleActionForBootCompleted(action, delay);
+ }
+
+ @Override
+ public boolean isWakeupCausedByTimer() {
+ return mSystemStateInterface.isWakeupCausedByTimer();
+ }
+
+ @Override
+ public boolean isSystemSupportingDeepSleep() {
+ return mSystemStateInterface.isSystemSupportingDeepSleep();
+ }
+
+ public final static class Builder {
+ private DisplayInterface mDisplayInterface;
+ private IOInterface mIOInterface;
+ private StorageMonitoringInterface mStorageMonitoringInterface;
+ private SystemStateInterface mSystemStateInterface;
+ private TimeInterface mTimeInterface;
+ private WakeLockInterface mWakeLockInterface;
+
+ private Builder() {}
+
+ public static Builder newSystemInterface() {
+ return new Builder();
+ }
+
+ public static Builder defaultSystemInterface(Context context) {
+ context = Objects.requireNonNull(context);
+ Builder builder = newSystemInterface();
+ builder.withWakeLockInterface(new WakeLockInterface.DefaultImpl(context));
+ builder.withDisplayInterface(new DisplayInterface.DefaultImpl(context,
+ builder.mWakeLockInterface));
+ builder.withIOInterface(new IOInterface.DefaultImpl(context));
+ builder.withStorageMonitoringInterface(new StorageMonitoringInterface.DefaultImpl());
+ builder.withSystemStateInterface(new SystemStateInterface.DefaultImpl(context));
+ return builder.withTimeInterface(new TimeInterface.DefaultImpl());
+ }
+
+ public static Builder fromBuilder(Builder otherBuilder) {
+ return newSystemInterface()
+ .withDisplayInterface(otherBuilder.mDisplayInterface)
+ .withIOInterface(otherBuilder.mIOInterface)
+ .withStorageMonitoringInterface(otherBuilder.mStorageMonitoringInterface)
+ .withSystemStateInterface(otherBuilder.mSystemStateInterface)
+ .withTimeInterface(otherBuilder.mTimeInterface)
+ .withWakeLockInterface(otherBuilder.mWakeLockInterface);
+ }
+
+ public Builder withDisplayInterface(DisplayInterface displayInterface) {
+ mDisplayInterface = displayInterface;
+ return this;
+ }
+
+ public Builder withIOInterface(IOInterface ioInterface) {
+ mIOInterface = ioInterface;
+ return this;
+ }
+
+ public Builder withStorageMonitoringInterface(StorageMonitoringInterface
+ storageMonitoringInterface) {
+ mStorageMonitoringInterface = storageMonitoringInterface;
+ return this;
+ }
+
+ public Builder withSystemStateInterface(SystemStateInterface systemStateInterface) {
+ mSystemStateInterface = systemStateInterface;
+ return this;
+ }
+
+ public Builder withTimeInterface(TimeInterface timeInterface) {
+ mTimeInterface = timeInterface;
+ return this;
+ }
+
+ public Builder withWakeLockInterface(WakeLockInterface wakeLockInterface) {
+ mWakeLockInterface = wakeLockInterface;
+ return this;
+ }
+
+ public SystemInterface build() {
+ return new SystemInterface(Objects.requireNonNull(mDisplayInterface),
+ Objects.requireNonNull(mIOInterface),
+ Objects.requireNonNull(mStorageMonitoringInterface),
+ Objects.requireNonNull(mSystemStateInterface),
+ Objects.requireNonNull(mTimeInterface),
+ Objects.requireNonNull(mWakeLockInterface));
+ }
+ }
+}
diff --git a/service/src/com/android/car/systeminterface/SystemStateInterface.java b/service/src/com/android/car/systeminterface/SystemStateInterface.java
new file mode 100644
index 0000000..f7e1506
--- /dev/null
+++ b/service/src/com/android/car/systeminterface/SystemStateInterface.java
@@ -0,0 +1,114 @@
+/*
+ * 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.android.car.systeminterface;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.util.Pair;
+import com.android.car.procfsinspector.ProcessInfo;
+import com.android.car.procfsinspector.ProcfsInspector;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Interface that abstracts system status (booted, sleeping, ...) operations
+ */
+public interface SystemStateInterface {
+ void shutdown();
+ void enterDeepSleep(int wakeupTimeSec);
+ void scheduleActionForBootCompleted(Runnable action, Duration delay);
+
+ default boolean isWakeupCausedByTimer() {
+ //TODO bug: 32061842, check wake up reason and do necessary operation information should
+ // come from kernel. it can be either power on or wake up for maintenance
+ // power on will involve GPIO trigger from power controller
+ // its own wakeup will involve timer expiration.
+ return false;
+ }
+
+ default boolean isSystemSupportingDeepSleep() {
+ //TODO should return by checking some kernel suspend control sysfs, bug: 32061842
+ return false;
+ }
+
+ default List<ProcessInfo> getRunningProcesses() {
+ return ProcfsInspector.readProcessTable();
+ }
+
+ class DefaultImpl implements SystemStateInterface {
+ private final static Duration MIN_BOOT_COMPLETE_ACTION_DELAY = Duration.ofSeconds(10);
+
+ private final Context mContext;
+ private final PowerManager mPowerManager;
+ private List<Pair<Runnable, Duration>> mActionsList = new ArrayList<>();
+ private ScheduledExecutorService mExecutorService;
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
+ for (Pair<Runnable, Duration> action : mActionsList) {
+ mExecutorService.schedule(action.first,
+ action.second.toMillis(), TimeUnit.MILLISECONDS);
+ }
+ }
+ }
+ };
+
+ DefaultImpl(Context context) {
+ mContext = context;
+ mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ }
+
+ @Override
+ public void shutdown() {
+ mPowerManager.shutdown(false /* no confirm*/, null, true /* true */);
+ }
+
+ @Override
+ public void enterDeepSleep(int wakeupTimeSec) {
+ //TODO set wake up time, bug: 32061842
+ mPowerManager.goToSleep(SystemClock.uptimeMillis(),
+ PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN,
+ PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
+ }
+
+ @Override
+ public void scheduleActionForBootCompleted(Runnable action, Duration delay) {
+ if (MIN_BOOT_COMPLETE_ACTION_DELAY.compareTo(delay) < 0) {
+ // TODO: consider adding some degree of randomness here
+ delay = MIN_BOOT_COMPLETE_ACTION_DELAY;
+ }
+ if (mActionsList.isEmpty()) {
+ final int corePoolSize = 1;
+ mExecutorService = Executors.newScheduledThreadPool(corePoolSize);
+ IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
+ mContext.registerReceiver(mBroadcastReceiver, intentFilter);
+ }
+ mActionsList.add(Pair.create(action, delay));
+ }
+
+ }
+}
diff --git a/service/src/com/android/car/systeminterface/TimeInterface.java b/service/src/com/android/car/systeminterface/TimeInterface.java
new file mode 100644
index 0000000..dea1153
--- /dev/null
+++ b/service/src/com/android/car/systeminterface/TimeInterface.java
@@ -0,0 +1,57 @@
+/*
+ * 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.android.car.systeminterface;
+
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+
+import android.os.SystemClock;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Interface that abstracts time operations
+ */
+public interface TimeInterface {
+ public static final boolean INCLUDE_DEEP_SLEEP_TIME = true;
+ public static final boolean EXCLUDE_DEEP_SLEEP_TIME = false;
+
+ default long getUptime() {
+ return getUptime(EXCLUDE_DEEP_SLEEP_TIME);
+ }
+ default long getUptime(boolean includeDeepSleepTime) {
+ return includeDeepSleepTime ?
+ SystemClock.elapsedRealtime() :
+ SystemClock.uptimeMillis();
+ }
+
+ void scheduleAction(Runnable r, long delayMs);
+ void cancelAllActions();
+
+ class DefaultImpl implements TimeInterface {
+ private final ScheduledExecutorService mExecutor = newSingleThreadScheduledExecutor();
+
+ @Override
+ public void scheduleAction(Runnable r, long delayMs) {
+ mExecutor.scheduleAtFixedRate(r, delayMs, delayMs, TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ public void cancelAllActions() {
+ mExecutor.shutdownNow();
+ }
+ }
+}
diff --git a/service/src/com/android/car/systeminterface/WakeLockInterface.java b/service/src/com/android/car/systeminterface/WakeLockInterface.java
new file mode 100644
index 0000000..c7a5f8e
--- /dev/null
+++ b/service/src/com/android/car/systeminterface/WakeLockInterface.java
@@ -0,0 +1,75 @@
+/*
+ * 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.android.car.systeminterface;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import com.android.car.CarLog;
+
+/**
+ * Interface that abstracts wake lock operations
+ */
+public interface WakeLockInterface {
+ void releaseAllWakeLocks();
+ void switchToPartialWakeLock();
+ void switchToFullWakeLock();
+
+ class DefaultImpl implements WakeLockInterface {
+ private final WakeLock mPartialWakeLock;
+ private final WakeLock mFullWakeLock;
+
+ DefaultImpl(Context context) {
+ PowerManager powerManager =
+ (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ mFullWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK,
+ CarLog.TAG_POWER);
+ mPartialWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ CarLog.TAG_POWER);
+ }
+
+ @Override
+ public void switchToPartialWakeLock() {
+ if (!mPartialWakeLock.isHeld()) {
+ mPartialWakeLock.acquire();
+ }
+ if (mFullWakeLock.isHeld()) {
+ mFullWakeLock.release();
+ }
+ }
+
+ @Override
+ public void switchToFullWakeLock() {
+ if (!mFullWakeLock.isHeld()) {
+ mFullWakeLock.acquire();
+ }
+ if (mPartialWakeLock.isHeld()) {
+ mPartialWakeLock.release();
+ }
+ }
+
+ @Override
+ public void releaseAllWakeLocks() {
+ if (mPartialWakeLock.isHeld()) {
+ mPartialWakeLock.release();
+ }
+ if (mFullWakeLock.isHeld()) {
+ mFullWakeLock.release();
+ }
+ }
+ }
+}
diff --git a/libvehiclemonitor/java/Android.mk b/tests/DefaultStorageMonitoringCompanionApp/Android.mk
similarity index 60%
copy from libvehiclemonitor/java/Android.mk
copy to tests/DefaultStorageMonitoringCompanionApp/Android.mk
index 1ced365..8eb2a55 100644
--- a/libvehiclemonitor/java/Android.mk
+++ b/tests/DefaultStorageMonitoringCompanionApp/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2016 The Android Open Source Project
+# 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.
@@ -18,9 +18,22 @@
include $(CLEAR_VARS)
-LOCAL_MODULE := libvehiclemonitor-java
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := DefaultStorageMonitoringCompanionApp
+
+LOCAL_AAPT_FLAGS := --auto-add-overlay
+
LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)
+LOCAL_PROGUARD_ENABLED := disabled
-include $(BUILD_STATIC_JAVA_LIBRARY)
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_DEX_PREOPT := false
+
+LOCAL_JAVA_LIBRARIES += android.car
+
+include $(BUILD_PACKAGE)
diff --git a/tests/DefaultStorageMonitoringCompanionApp/AndroidManifest.xml b/tests/DefaultStorageMonitoringCompanionApp/AndroidManifest.xml
new file mode 100644
index 0000000..45380ce
--- /dev/null
+++ b/tests/DefaultStorageMonitoringCompanionApp/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?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.defaultstoragemonitoringcompanionapp">
+
+ <uses-permission android:name="android.car.permission.STORAGE_MONITORING" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme">
+ <activity
+ android:name=".MainActivity"
+ android:exported="true"
+ android:permission="android.car.permission.STORAGE_MONITORING">
+ </activity>
+ </application>
+
+</manifest>
diff --git a/tests/DefaultStorageMonitoringCompanionApp/res/layout/activity_main.xml b/tests/DefaultStorageMonitoringCompanionApp/res/layout/activity_main.xml
new file mode 100644
index 0000000..017ebcc
--- /dev/null
+++ b/tests/DefaultStorageMonitoringCompanionApp/res/layout/activity_main.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/notification"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:minLines="10"
+ android:textSize="24dp"
+ android:textColor="#ff0000"
+ android:text="Gathering data..."/>
+
+</RelativeLayout>
diff --git a/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-hdpi/ic_launcher.png b/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
--- /dev/null
+++ b/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-hdpi/ic_launcher_round.png b/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..9a078e3
--- /dev/null
+++ b/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-hdpi/ic_launcher_round.png
Binary files differ
diff --git a/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-mdpi/ic_launcher.png b/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
--- /dev/null
+++ b/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-mdpi/ic_launcher_round.png b/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..efc028a
--- /dev/null
+++ b/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-mdpi/ic_launcher_round.png
Binary files differ
diff --git a/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-xhdpi/ic_launcher.png b/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
--- /dev/null
+++ b/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-xhdpi/ic_launcher_round.png b/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..3af2608
--- /dev/null
+++ b/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-xhdpi/ic_launcher_round.png
Binary files differ
diff --git a/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-xxhdpi/ic_launcher.png b/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
--- /dev/null
+++ b/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-xxhdpi/ic_launcher_round.png b/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..9bec2e6
--- /dev/null
+++ b/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-xxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-xxxhdpi/ic_launcher.png b/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
--- /dev/null
+++ b/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-xxxhdpi/ic_launcher_round.png b/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..34947cd
--- /dev/null
+++ b/tests/DefaultStorageMonitoringCompanionApp/res/mipmap-xxxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/tests/DefaultStorageMonitoringCompanionApp/res/values/colors.xml b/tests/DefaultStorageMonitoringCompanionApp/res/values/colors.xml
new file mode 100644
index 0000000..5a077b3
--- /dev/null
+++ b/tests/DefaultStorageMonitoringCompanionApp/res/values/colors.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="colorPrimary">#3F51B5</color>
+ <color name="colorPrimaryDark">#303F9F</color>
+ <color name="colorAccent">#FF4081</color>
+</resources>
diff --git a/tests/DefaultStorageMonitoringCompanionApp/res/values/strings.xml b/tests/DefaultStorageMonitoringCompanionApp/res/values/strings.xml
new file mode 100644
index 0000000..8c0a9bb
--- /dev/null
+++ b/tests/DefaultStorageMonitoringCompanionApp/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+ <string name="app_name">Flash Storage Wear Notification</string>
+</resources>
diff --git a/tests/DefaultStorageMonitoringCompanionApp/res/values/styles.xml b/tests/DefaultStorageMonitoringCompanionApp/res/values/styles.xml
new file mode 100644
index 0000000..a7a0615
--- /dev/null
+++ b/tests/DefaultStorageMonitoringCompanionApp/res/values/styles.xml
@@ -0,0 +1,8 @@
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ </style>
+
+</resources>
diff --git a/tests/DefaultStorageMonitoringCompanionApp/src/com/google/android/car/defaultstoragemonitoringcompanionapp/MainActivity.java b/tests/DefaultStorageMonitoringCompanionApp/src/com/google/android/car/defaultstoragemonitoringcompanionapp/MainActivity.java
new file mode 100644
index 0000000..bcec38b
--- /dev/null
+++ b/tests/DefaultStorageMonitoringCompanionApp/src/com/google/android/car/defaultstoragemonitoringcompanionapp/MainActivity.java
@@ -0,0 +1,172 @@
+/*
+ * 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.defaultstoragemonitoringcompanionapp;
+
+import android.app.Activity;
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.storagemonitoring.CarStorageMonitoringManager;
+import android.car.storagemonitoring.WearEstimate;
+import android.car.storagemonitoring.WearEstimateChange;
+import android.content.ComponentName;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+import android.widget.TextView;
+import java.time.Duration;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.format.FormatStyle;
+import java.util.List;
+import java.util.Locale;
+
+public class MainActivity extends Activity {
+ private static final boolean DEBUG = false;
+ private static final String TAG = MainActivity.class.getSimpleName();
+
+ private final ServiceConnection mCarConnectionListener = new ServiceConnection() {
+ private String wearLevelToString(int wearLevel) {
+ if (wearLevel == WearEstimate.UNKNOWN) return "unknown";
+ return wearLevel + "%";
+ }
+
+ private String durationPairToString(long value1, String name1,
+ long value2, String name2) {
+ if (value1 > 0) {
+ String s = value1 + " " + name1;
+ if (value2 > 0) {
+ s += " and " + value2 + " " + name2;
+ }
+ return s;
+ }
+ return null;
+ }
+
+ private String durationToString(Duration duration) {
+ final long days = duration.toDays();
+ duration = duration.minusDays(days);
+ final long hours = duration.toHours();
+ duration = duration.minusHours(hours);
+ final long minutes = duration.toMinutes();
+
+ // for a week or more, just return days
+ if (days >= 7) {
+ return days + " days";
+ }
+
+ // otherwise try a few pairs of units
+ final String daysHours = durationPairToString(days, "days", hours, "hours");
+ if (daysHours != null) return daysHours;
+ final String hoursMinutes = durationPairToString(hours, "hours", minutes, "minutes");
+ if (hoursMinutes != null) return hoursMinutes;
+
+ // either minutes, or less than a minute
+ if (minutes > 0) {
+ return minutes + " minutes";
+ } else {
+ return "less than a minute";
+ }
+ }
+
+ private String wearChangeToString(WearEstimateChange wearEstimateChange) {
+ final int oldLevel = Math.max(wearEstimateChange.oldEstimate.typeA,
+ wearEstimateChange.oldEstimate.typeB);
+ final int newLevel = Math.max(wearEstimateChange.newEstimate.typeA,
+ wearEstimateChange.newEstimate.typeB);
+
+ final String oldLevelString = wearLevelToString(oldLevel);
+ final String newLevelString = wearLevelToString(newLevel);
+
+ final DateTimeFormatter formatter =
+ DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT).withZone(
+ ZoneId.systemDefault()).withLocale(Locale.getDefault());
+
+ final String wallClockTimeAtChange = formatter.format(wearEstimateChange.dateAtChange);
+ final String uptimeAtChange = durationToString(
+ Duration.ofMillis(wearEstimateChange.uptimeAtChange));
+
+ return String.format(
+ "Wear level went from %s to %s.\nThe vehicle has been running for %s.\nWall clock time: %s",
+ oldLevelString,
+ newLevelString,
+ uptimeAtChange,
+ wallClockTimeAtChange);
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.d(TAG, "Connected to " + name.flattenToString());
+
+ try {
+ CarStorageMonitoringManager storageMonitoringManager =
+ (CarStorageMonitoringManager) mCar.getCarManager(
+ Car.STORAGE_MONITORING_SERVICE);
+ Log.d(TAG, "Acquired a CarStorageMonitoringManager " + storageMonitoringManager);
+ List<WearEstimateChange> wearEstimateChanges = storageMonitoringManager
+ .getWearEstimateHistory();
+ if (wearEstimateChanges.isEmpty()) {
+ finish();
+ }
+
+ WearEstimateChange currentChange = wearEstimateChanges
+ .get(wearEstimateChanges.size() - 1);
+
+ if (!DEBUG && currentChange.isAcceptableDegradation) {
+ finish();
+ }
+
+ mNotificationTextView.setText(wearChangeToString(currentChange));
+ } 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;
+ }
+ };
+
+ private final Handler mHandler = new Handler();
+
+ private Car mCar = null;
+ private TextView mNotificationTextView = null;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ mNotificationTextView = findViewById(R.id.notification);
+
+ mCar = Car.createCar(this, mCarConnectionListener);
+ mCar.connect();
+
+ mHandler.postDelayed(this::finish, 30000);
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (mCar != null) {
+ mCar.disconnect();
+ }
+ super.onDestroy();
+ }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
index 2a96898..db09cba 100644
--- a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
+++ b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
@@ -33,6 +33,7 @@
<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.car.permission.STORAGE_MONITORING" />
<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" />
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/storagewear.xml b/tests/EmbeddedKitchenSinkApp/res/layout/storagewear.xml
new file mode 100644
index 0000000..bf36b4a
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/storagewear.xml
@@ -0,0 +1,57 @@
+<?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="match_parent"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/storage_wear_info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Storage Wear Information"
+ android:textSize="24dp"
+ android:textColor="#ff0000"
+ android:minLines="5"/>
+ <ListView
+ android:id="@+id/storage_events_list"
+ android:layout_width="match_parent"
+ android:layout_height="75dp"/>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <Button
+ android:id="@+id/write_one_megabyte"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="Write 1M"/>
+ <Button
+ android:id="@+id/write_one_kilobyte"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="Write 1K"/>
+ </LinearLayout>
+ <TextView
+ android:id="@+id/free_disk_space"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Free Disk Space: 1 byte"
+ android:textSize="24dp"
+ android:textColor="#ff0000"
+ android:minLines="5"/>
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/wear_estimate_change_textview.xml b/tests/EmbeddedKitchenSinkApp/res/layout/wear_estimate_change_textview.xml
new file mode 100644
index 0000000..0b09562
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/wear_estimate_change_textview.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/wear_estimate_change_textview"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Wear Estimate Change"
+ android:textSize="18dp"
+ android:textColor="#ff0000"
+ android:minLines="2"/>
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 7dddd1f..064d06d 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarEmulator.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/CarEmulator.java
@@ -1,3 +1,4 @@
+
/*
* Copyright (C) 2015 The Android Open Source Project
*
@@ -35,7 +36,7 @@
import android.util.SparseIntArray;
import com.android.car.ICarImpl;
-import com.android.car.SystemInterface;
+import com.android.car.systeminterface.SystemInterface;
import com.android.car.vehiclehal.VehiclePropValueBuilder;
import com.android.car.vehiclehal.test.MockedVehicleHal;
import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
@@ -66,7 +67,8 @@
private CarEmulator(Context context) {
mHalEmulator = new MockedVehicleHal();
ICarImpl carService = new ICarImpl(context, mHalEmulator,
- SystemInterface.getDefault(context), null /* error notifier */);
+ SystemInterface.Builder.defaultSystemInterface(context).build(),
+ null /* error notifier */);
mCar = new Car(context, carService, null /* Handler */);
}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
index e68c3fa..c1dd824 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
@@ -49,6 +49,7 @@
import com.google.android.car.kitchensink.radio.RadioTestFragment;
import com.google.android.car.kitchensink.sensor.SensorsTestFragment;
import com.google.android.car.kitchensink.setting.CarServiceSettingsActivity;
+import com.google.android.car.kitchensink.storagelifetime.StorageLifetimeFragment;
import com.google.android.car.kitchensink.touch.TouchTestFragment;
import com.google.android.car.kitchensink.volume.VolumeTestFragment;
import java.util.ArrayList;
@@ -147,6 +148,7 @@
add("orientation test", OrientationTestFragment.class);
add("bluetooth headset",BluetoothHeadsetFragment.class);
add("bluetooth messaging test", MapMceTestFragment.class);
+ add("storage lifetime", StorageLifetimeFragment.class);
add("car service settings", () -> {
Intent intent = new Intent(KitchenSinkActivity.this,
CarServiceSettingsActivity.class);
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/storagelifetime/StorageLifetimeFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/storagelifetime/StorageLifetimeFragment.java
new file mode 100644
index 0000000..336cc33
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/storagelifetime/StorageLifetimeFragment.java
@@ -0,0 +1,141 @@
+/*
+ * 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.storagelifetime;
+
+import android.annotation.Nullable;
+import android.car.Car;
+import android.car.storagemonitoring.CarStorageMonitoringManager;
+import android.car.storagemonitoring.WearEstimateChange;
+import android.database.DataSetObserver;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.os.StatFs;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+import com.google.android.car.kitchensink.KitchenSinkActivity;
+import com.google.android.car.kitchensink.R;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+public class StorageLifetimeFragment extends Fragment {
+ private static final String FILE_NAME = "storage.bin";
+ private static final String TAG = "CAR.STORAGELIFETIME.KS";
+
+ private static final int KILOBYTE = 1024;
+ private static final int MEGABYTE = 1024 * 1024;
+
+ private StatFs mStatFs;
+ private KitchenSinkActivity mActivity;
+ private TextView mStorageWearInfo;
+ private ListView mStorageChangesHistory;
+ private TextView mFreeSpaceInfo;
+ private CarStorageMonitoringManager mStorageManager;
+
+ // TODO(egranata): put this somewhere more useful than KitchenSink
+ private static String preEolToString(int preEol) {
+ switch (preEol) {
+ case 1: return "normal";
+ case 2: return "warning";
+ case 3: return "urgent";
+ default:
+ return "unknown";
+ }
+ }
+
+ private void writeBytesToFile(int size) {
+ try {
+ byte[] data = new byte[size];
+ SecureRandom.getInstanceStrong().nextBytes(data);
+ Path filePath = new File(mActivity.getFilesDir(), FILE_NAME).toPath();
+ if (Files.notExists(filePath)) {
+ Files.createFile(filePath);
+ }
+ Files.write(filePath,
+ data,
+ StandardOpenOption.APPEND);
+ } catch (NoSuchAlgorithmException | IOException e) {
+ Log.w(TAG, "could not append data", e);
+ }
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater,
+ @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.storagewear, container, false);
+ mActivity = (KitchenSinkActivity) getHost();
+ mStorageWearInfo = view.findViewById(R.id.storage_wear_info);
+ mStorageChangesHistory = view.findViewById(R.id.storage_events_list);
+ mFreeSpaceInfo = view.findViewById(R.id.free_disk_space);
+
+ view.findViewById(R.id.write_one_kilobyte).setOnClickListener(
+ v -> writeBytesToFile(KILOBYTE));
+
+ view.findViewById(R.id.write_one_megabyte).setOnClickListener(
+ v -> writeBytesToFile(MEGABYTE));
+
+ return view;
+ }
+
+ private void reloadInfo() {
+ try {
+ mStatFs = new StatFs(mActivity.getFilesDir().getAbsolutePath());
+
+ mStorageManager =
+ (CarStorageMonitoringManager) mActivity.getCar().getCarManager(
+ Car.STORAGE_MONITORING_SERVICE);
+
+ mStorageWearInfo.setText("Wear estimate: " +
+ mStorageManager.getWearEstimate() + "\nPre EOL indicator: " +
+ preEolToString(mStorageManager.getPreEolIndicatorStatus()));
+
+ mStorageChangesHistory.setAdapter(new ArrayAdapter(mActivity,
+ R.layout.wear_estimate_change_textview,
+ mStorageManager.getWearEstimateHistory().toArray()));
+
+ mFreeSpaceInfo.setText("Available blocks: " + mStatFs.getAvailableBlocksLong() +
+ "\nBlock size: " + mStatFs.getBlockSizeLong() + " bytes" +
+ "\nfor a total free space of: " +
+ (mStatFs.getBlockSizeLong() * mStatFs.getAvailableBlocksLong() / MEGABYTE) + "MB");
+ } catch (android.car.CarNotConnectedException|
+ android.support.car.CarNotConnectedException e) {
+ Log.e(TAG, "Car not connected or not supported", e);
+ }
+
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ reloadInfo();
+ }
+}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/volume/VolumeTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/volume/VolumeTestFragment.java
index 0d65500..874bc64 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/volume/VolumeTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/volume/VolumeTestFragment.java
@@ -18,11 +18,14 @@
import android.car.Car;
import android.car.CarNotConnectedException;
import android.car.media.CarAudioManager;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.ServiceConnection;
import android.media.AudioManager;
import android.media.IVolumeController;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.support.annotation.Nullable;
@@ -36,10 +39,9 @@
import android.widget.Button;
import android.widget.ListView;
-import com.google.android.car.kitchensink.CarEmulator;
import com.google.android.car.kitchensink.R;
-public class VolumeTestFragment extends Fragment{
+public class VolumeTestFragment extends Fragment {
private static final String TAG = "CarVolumeTest";
private static final int MSG_VOLUME_CHANGED = 0;
private static final int MSG_REQUEST_FOCUS = 1;
@@ -52,7 +54,6 @@
private CarAudioManager mCarAudioManager;
private Car mCar;
- private CarEmulator mCarEmulator;
private Button mVolumeUp;
private Button mVolumeDown;
@@ -140,6 +141,26 @@
default: return "Unknown";
}
}
+ private final ServiceConnection mCarConnectionCallback =
+ new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder binder) {
+ Log.d(TAG, "Connected to Car Service");
+ try {
+ mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
+ initVolumeInfo();
+ mCarAudioManager.setVolumeController(mVolumeController);
+
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.d(TAG, "Disconnect from Car Service");
+ }
+ };
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -160,33 +181,8 @@
}
});
- mCarEmulator = CarEmulator.create(getContext());
- mCar = mCarEmulator.getCar();
- try {
- mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
- initVolumeInfo();
- mCarAudioManager.setVolumeController(mVolumeController);
- } catch (CarNotConnectedException e) {
- throw new RuntimeException(e); // Should never occur in car emulator.
- }
-
- mVolumeUp = (Button) v.findViewById(R.id.volume_up);
- mVolumeDown = (Button) v.findViewById(R.id.volume_down);
-
- mVolumeUp.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mCarEmulator.injectKey(KeyEvent.KEYCODE_VOLUME_UP);
- }
- });
-
- mVolumeDown.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mCarEmulator.injectKey(KeyEvent.KEYCODE_VOLUME_DOWN);
- }
- });
-
+ mCar = Car.createCar(getActivity(), mCarConnectionCallback);
+ mCar.connect();
return v;
}
@@ -214,7 +210,8 @@
return;
}
try {
- mCarAudioManager.setStreamVolume(logicalStream, volume, 0);
+ mCarAudioManager.setStreamVolume(logicalStream, volume,
+ AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_PLAY_SOUND);
} catch (CarNotConnectedException e) {
Log.e(TAG, "car not connected", e);
}
diff --git a/libvehiclemonitor/java/Android.mk b/tests/VmsPublisherClientSample/Android.mk
similarity index 64%
copy from libvehiclemonitor/java/Android.mk
copy to tests/VmsPublisherClientSample/Android.mk
index 1ced365..2aa6c40 100644
--- a/libvehiclemonitor/java/Android.mk
+++ b/tests/VmsPublisherClientSample/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2016 The Android Open Source Project
+# 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.
@@ -18,9 +18,20 @@
include $(CLEAR_VARS)
-LOCAL_MODULE := libvehiclemonitor-java
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := VmsPublisherClientSample
+
LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)
+LOCAL_PRIVILEGED_MODULE := true
-include $(BUILD_STATIC_JAVA_LIBRARY)
+LOCAL_CERTIFICATE := testkey
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_JAVA_LIBRARIES += android.car
+
+include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/tests/VmsPublisherClientSample/AndroidManifest.xml b/tests/VmsPublisherClientSample/AndroidManifest.xml
new file mode 100644
index 0000000..fdc1a31
--- /dev/null
+++ b/tests/VmsPublisherClientSample/AndroidManifest.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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.car.vms.publisher">
+
+ <uses-permission android:name="android.car.permission.VMS_PUBLISHER" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+ <uses-permission android:name="android.permission.CAMERA"/>
+
+ <uses-sdk android:minSdkVersion="25" android:targetSdkVersion='25'/>
+
+ <application android:label="@string/app_name"
+ android:icon="@mipmap/ic_launcher"
+ android:directBootAware="true">
+ <service android:name=".VmsPublisherClientSampleService"
+ android:exported="true"
+ android:singleUser="true">
+ </service>
+ </application>
+</manifest>
diff --git a/tests/VmsPublisherClientSample/res/mipmap-hdpi/ic_launcher.png b/tests/VmsPublisherClientSample/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
--- /dev/null
+++ b/tests/VmsPublisherClientSample/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/VmsPublisherClientSample/res/mipmap-mdpi/ic_launcher.png b/tests/VmsPublisherClientSample/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
--- /dev/null
+++ b/tests/VmsPublisherClientSample/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/tests/VmsPublisherClientSample/res/mipmap-xhdpi/ic_launcher.png b/tests/VmsPublisherClientSample/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
--- /dev/null
+++ b/tests/VmsPublisherClientSample/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/VmsPublisherClientSample/res/mipmap-xxhdpi/ic_launcher.png b/tests/VmsPublisherClientSample/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
--- /dev/null
+++ b/tests/VmsPublisherClientSample/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/VmsPublisherClientSample/res/mipmap-xxxhdpi/ic_launcher.png b/tests/VmsPublisherClientSample/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
--- /dev/null
+++ b/tests/VmsPublisherClientSample/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/VmsPublisherClientSample/res/values/strings.xml b/tests/VmsPublisherClientSample/res/values/strings.xml
new file mode 100644
index 0000000..df8bf05
--- /dev/null
+++ b/tests/VmsPublisherClientSample/res/values/strings.xml
@@ -0,0 +1,18 @@
+<?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="app_name">VmsPublisherClientSample</string>
+</resources>
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
new file mode 100644
index 0000000..e235f1e
--- /dev/null
+++ b/tests/VmsPublisherClientSample/src/com/google/android/car/vms/publisher/VmsPublisherClientSampleService.java
@@ -0,0 +1,81 @@
+/*
+ * 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.vms.publisher;
+
+import android.car.vms.VmsLayer;
+import android.car.vms.VmsPublisherClientService;
+import android.car.vms.VmsSubscriptionState;
+import android.os.Handler;
+import android.os.Message;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * This service is launched during the initialization of the VMS publisher service.
+ * Once onVmsPublisherServiceReady is invoked, it starts publishing a single byte every second.
+ */
+public class VmsPublisherClientSampleService extends VmsPublisherClientService {
+ public static final int PUBLISH_EVENT = 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);
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == PUBLISH_EVENT && mInitialized.get()) {
+ periodicPublish();
+ }
+ }
+ };
+
+ /**
+ * Notifies that the publisher services are ready to be used: {@link #publish(VmsLayer, byte[])}
+ * and {@link #getSubscriptions()}.
+ */
+ @Override
+ public void onVmsPublisherServiceReady() {
+ VmsSubscriptionState subscriptionState = getSubscriptions();
+ onVmsSubscriptionChange(subscriptionState);
+ }
+
+ @Override
+ public void onVmsSubscriptionChange(VmsSubscriptionState subscriptionState) {
+ if (mInitialized.compareAndSet(false, true)) {
+ for (VmsLayer layer : subscriptionState.getLayers()) {
+ if (layer.equals(TEST_LAYER)) {
+ mHandler.sendEmptyMessage(PUBLISH_EVENT);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mInitialized.set(false);
+ mHandler.removeMessages(PUBLISH_EVENT);
+ }
+
+ private void periodicPublish() {
+ publish(TEST_LAYER, PUBLISHER_ID, new byte[]{mCounter});
+ ++mCounter;
+ mHandler.sendEmptyMessageDelayed(PUBLISH_EVENT, 1000);
+ }
+}
diff --git a/libvehiclemonitor/java/Android.mk b/tests/VmsSubscriberClientSample/Android.mk
similarity index 60%
copy from libvehiclemonitor/java/Android.mk
copy to tests/VmsSubscriberClientSample/Android.mk
index 1ced365..f59e267 100644
--- a/libvehiclemonitor/java/Android.mk
+++ b/tests/VmsSubscriberClientSample/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2016 The Android Open Source Project
+# 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.
@@ -18,9 +18,22 @@
include $(CLEAR_VARS)
-LOCAL_MODULE := libvehiclemonitor-java
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := VmsSubscriberClientSample
+
LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)
+LOCAL_PRIVILEGED_MODULE := true
-include $(BUILD_STATIC_JAVA_LIBRARY)
+LOCAL_CERTIFICATE := platform
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_JAVA_LIBRARIES += android.car
+
+include packages/services/Car/car-support-lib/car-support.mk
+
+include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/tests/VmsSubscriberClientSample/AndroidManifest.xml b/tests/VmsSubscriberClientSample/AndroidManifest.xml
new file mode 100644
index 0000000..bc798d7
--- /dev/null
+++ b/tests/VmsSubscriberClientSample/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?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"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ package="com.google.android.car.vms.subscriber"
+ android:sharedUserId="android.uid.system">
+ <uses-sdk android:minSdkVersion="25" android:targetSdkVersion='25'/>
+
+ <application android:label="@string/app_name"
+ android:icon="@mipmap/ic_launcher">
+ <meta-data
+ android:name="android.car.application"
+ android:resource="@xml/automotive_app_desc"/>
+ <activity android:name=".VmsSubscriberClientSampleActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
\ No newline at end of file
diff --git a/tests/VmsSubscriberClientSample/res/layout/activity_main.xml b/tests/VmsSubscriberClientSample/res/layout/activity_main.xml
new file mode 100644
index 0000000..ce05a4d
--- /dev/null
+++ b/tests/VmsSubscriberClientSample/res/layout/activity_main.xml
@@ -0,0 +1,20 @@
+<?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:paddingTop="@dimen/activity_vertical_margin"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ tools:context="vms.apps.android.google.com.java.myapplication.MainActivity">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:text=""
+ android:id="@+id/textview"/>
+</RelativeLayout>
diff --git a/tests/VmsSubscriberClientSample/res/mipmap-hdpi/ic_launcher.png b/tests/VmsSubscriberClientSample/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
--- /dev/null
+++ b/tests/VmsSubscriberClientSample/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/VmsSubscriberClientSample/res/mipmap-mdpi/ic_launcher.png b/tests/VmsSubscriberClientSample/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
--- /dev/null
+++ b/tests/VmsSubscriberClientSample/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/tests/VmsSubscriberClientSample/res/mipmap-xhdpi/ic_launcher.png b/tests/VmsSubscriberClientSample/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
--- /dev/null
+++ b/tests/VmsSubscriberClientSample/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/VmsSubscriberClientSample/res/mipmap-xxhdpi/ic_launcher.png b/tests/VmsSubscriberClientSample/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
--- /dev/null
+++ b/tests/VmsSubscriberClientSample/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/VmsSubscriberClientSample/res/mipmap-xxxhdpi/ic_launcher.png b/tests/VmsSubscriberClientSample/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
--- /dev/null
+++ b/tests/VmsSubscriberClientSample/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/VmsSubscriberClientSample/res/values-w820dp/dimens.xml b/tests/VmsSubscriberClientSample/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..308a194
--- /dev/null
+++ b/tests/VmsSubscriberClientSample/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/VmsSubscriberClientSample/res/values/colors.xml b/tests/VmsSubscriberClientSample/res/values/colors.xml
new file mode 100644
index 0000000..5a077b3
--- /dev/null
+++ b/tests/VmsSubscriberClientSample/res/values/colors.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="colorPrimary">#3F51B5</color>
+ <color name="colorPrimaryDark">#303F9F</color>
+ <color name="colorAccent">#FF4081</color>
+</resources>
diff --git a/tests/VmsSubscriberClientSample/res/values/dimens.xml b/tests/VmsSubscriberClientSample/res/values/dimens.xml
new file mode 100644
index 0000000..acf94cc
--- /dev/null
+++ b/tests/VmsSubscriberClientSample/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/VmsSubscriberClientSample/res/values/strings.xml b/tests/VmsSubscriberClientSample/res/values/strings.xml
new file mode 100644
index 0000000..24df55e
--- /dev/null
+++ b/tests/VmsSubscriberClientSample/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+ <string name="app_name">VmsSubscriberClientSample</string>
+</resources>
diff --git a/tests/VmsSubscriberClientSample/res/values/styles.xml b/tests/VmsSubscriberClientSample/res/values/styles.xml
new file mode 100644
index 0000000..a7a0615
--- /dev/null
+++ b/tests/VmsSubscriberClientSample/res/values/styles.xml
@@ -0,0 +1,8 @@
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ </style>
+
+</resources>
diff --git a/tests/VmsSubscriberClientSample/res/xml/automotive_app_desc.xml b/tests/VmsSubscriberClientSample/res/xml/automotive_app_desc.xml
new file mode 100644
index 0000000..b10ddd0
--- /dev/null
+++ b/tests/VmsSubscriberClientSample/res/xml/automotive_app_desc.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<automotiveApp>
+ <uses name="service" />
+ <uses name="projection" />
+ <uses name="activity" class="com.google.android.car.vms.subscriber.VmsSubscriberClientSampleActivity" />
+</automotiveApp>
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
new file mode 100644
index 0000000..3ac0707
--- /dev/null
+++ b/tests/VmsSubscriberClientSample/src/com/google/android/car/vms/subscriber/VmsSubscriberClientSampleActivity.java
@@ -0,0 +1,113 @@
+/*
+ * 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.vms.subscriber;
+
+import android.app.Activity;
+import android.car.vms.VmsLayer;
+import android.car.vms.VmsSubscriberManager;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.support.car.Car;
+import android.support.car.CarConnectionCallback;
+import android.util.Log;
+import android.widget.TextView;
+
+import java.util.List;
+
+/**
+ * Connects to the Car service during onCreate. CarConnectionCallback.onConnected is invoked when
+ * the connection is ready. Then, it subscribes to a VMS layer/version and updates the TextView when
+ * a message is received.
+ */
+public class VmsSubscriberClientSampleActivity extends Activity {
+ 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, 0);
+
+ private Car mCarApi;
+ private TextView mTextView;
+ private VmsSubscriberManager mVmsSubscriberManager;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ mTextView = (TextView) findViewById(R.id.textview);
+ if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ mCarApi = Car.createCar(this, mCarConnectionCallback);
+ mCarApi.connect();
+ } else {
+ Log.d(TAG, "No automotive feature.");
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mCarApi != null) {
+ mCarApi.disconnect();
+ }
+ Log.i(TAG, "onDestroy");
+ }
+
+ private final CarConnectionCallback mCarConnectionCallback = new CarConnectionCallback() {
+ @Override
+ public void onConnected(Car car) {
+ Log.d(TAG, "Connected to Car Service");
+ mVmsSubscriberManager = getVmsSubscriberManager();
+ configureSubscriptions(mVmsSubscriberManager);
+ }
+
+ @Override
+ public void onDisconnected(Car car) {
+ Log.d(TAG, "Disconnect from Car Service");
+ }
+
+ private VmsSubscriberManager getVmsSubscriberManager() {
+ try {
+ return (VmsSubscriberManager) mCarApi.getCarManager(
+ android.car.Car.VMS_SUBSCRIBER_SERVICE);
+ } catch (android.support.car.CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
+ }
+ return null;
+ }
+
+ private void configureSubscriptions(VmsSubscriberManager vmsSubscriberManager) {
+ try {
+ vmsSubscriberManager.registerClientCallback(mClientCallback);
+ vmsSubscriberManager.subscribe(TEST_LAYER);
+ } catch (android.car.CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
+ }
+ }
+ };
+
+ 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 onLayersAvailabilityChanged(List<VmsLayer> availableLayers) {
+ mTextView.setText(String.valueOf(availableLayers));
+ }
+ };
+}
diff --git a/tests/carservice_test/Android.mk b/tests/carservice_test/Android.mk
index 4be5354..9203cbd 100644
--- a/tests/carservice_test/Android.mk
+++ b/tests/carservice_test/Android.mk
@@ -41,7 +41,8 @@
vehicle-hal-support-lib \
car-systemtest \
android-support-test \
- android.hardware.automotive.vehicle-V2.0-java-static
+ android.hardware.automotive.vehicle-V2.0-java \
+ com.android.car.test.utils
LOCAL_JAVA_LIBRARIES := android.car android.test.runner
diff --git a/tests/carservice_test/AndroidManifest.xml b/tests/carservice_test/AndroidManifest.xml
index 6a1e2bf..a34c1c6 100644
--- a/tests/carservice_test/AndroidManifest.xml
+++ b/tests/carservice_test/AndroidManifest.xml
@@ -24,24 +24,29 @@
<uses-permission android:name="android.car.permission.CONTROL_APP_BLOCKING" />
<uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />
<uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_SETTINGS" />
+ <uses-permission android:name="android.car.permission.STORAGE_MONITORING" />
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.android.car.test"
android:label="Tests for Car APIs"/>
<application android:label="CarServiceTest">
<uses-library android:name="android.test.runner" />
- <service android:name="com.android.car.test.TestAppBlockingPolicyService"
+ <service android:name="com.android.car.TestAppBlockingPolicyService"
android:permission="android.car.permission.CONTROL_APP_BLOCKING">
<intent-filter>
<action android:name="android.car.content.pm.CarAppBlockingPolicyService"/>
</intent-filter>
</service>
- <activity android:name="com.android.car.test.SystemActivityMonitoringServiceTest$ActivityA" />
- <activity android:name="com.android.car.test.SystemActivityMonitoringServiceTest$ActivityB"
- android:taskAffinity="com.android.car.carservicetest.activity"/>
- <activity android:name="com.android.car.test.SystemActivityMonitoringServiceTest$ActivityC"
- android:process="com.android.car.carservicetest.activityC"/>
- <activity android:name="com.android.car.test.SystemActivityMonitoringServiceTest$BlockingActivity"
- android:taskAffinity="com.android.car.carservicetest.block"/>
+ <service android:name="com.android.car.SimpleVmsPublisherClientService"
+ android:exported="true"
+ />
+ <service android:name="com.android.car.VmsPublisherClientMockService"
+ android:exported="true" />
+
+ <activity android:name="com.android.car.SystemActivityMonitoringServiceTest$ActivityA"/>
+ <activity android:name="com.android.car.SystemActivityMonitoringServiceTest$ActivityB"/>
+ <activity android:name="com.android.car.SystemActivityMonitoringServiceTest$ActivityC"/>
+ <activity android:name="com.android.car.SystemActivityMonitoringServiceTest$BlockingActivity"
+ android:taskAffinity="com.android.car.carservicetest.block"/>
</application>
</manifest>
diff --git a/tests/carservice_test/src/com/android/car/test/AppFocusTest.java b/tests/carservice_test/src/com/android/car/AppFocusTest.java
similarity index 99%
rename from tests/carservice_test/src/com/android/car/test/AppFocusTest.java
rename to tests/carservice_test/src/com/android/car/AppFocusTest.java
index b63383e..d9bce26 100644
--- a/tests/carservice_test/src/com/android/car/test/AppFocusTest.java
+++ b/tests/carservice_test/src/com/android/car/AppFocusTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.car.test;
+package com.android.car;
import android.car.Car;
import android.car.CarAppFocusManager;
diff --git a/tests/carservice_test/src/com/android/car/test/AudioRoutingPolicyTest.java b/tests/carservice_test/src/com/android/car/AudioRoutingPolicyTest.java
similarity index 99%
rename from tests/carservice_test/src/com/android/car/test/AudioRoutingPolicyTest.java
rename to tests/carservice_test/src/com/android/car/AudioRoutingPolicyTest.java
index 7f4aeac..357da1d 100644
--- a/tests/carservice_test/src/com/android/car/test/AudioRoutingPolicyTest.java
+++ b/tests/carservice_test/src/com/android/car/AudioRoutingPolicyTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.car.test;
+package com.android.car;
import android.hardware.automotive.vehicle.V2_0.VehicleAudioContextFlag;
import android.hardware.automotive.vehicle.V2_0.VehicleAudioRoutingPolicyIndex;
diff --git a/tests/carservice_test/src/com/android/car/test/AudioTestUtils.java b/tests/carservice_test/src/com/android/car/AudioTestUtils.java
similarity index 98%
rename from tests/carservice_test/src/com/android/car/test/AudioTestUtils.java
rename to tests/carservice_test/src/com/android/car/AudioTestUtils.java
index 8c3de7c..459daa2 100644
--- a/tests/carservice_test/src/com/android/car/test/AudioTestUtils.java
+++ b/tests/carservice_test/src/com/android/car/AudioTestUtils.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.car.test;
+package com.android.car;
import android.media.AudioAttributes;
import android.media.AudioFocusRequest;
diff --git a/tests/carservice_test/src/com/android/car/test/CarAudioExtFocusTest.java b/tests/carservice_test/src/com/android/car/CarAudioExtFocusTest.java
similarity index 97%
rename from tests/carservice_test/src/com/android/car/test/CarAudioExtFocusTest.java
rename to tests/carservice_test/src/com/android/car/CarAudioExtFocusTest.java
index 71e0d04..46e1df6 100644
--- a/tests/carservice_test/src/com/android/car/test/CarAudioExtFocusTest.java
+++ b/tests/carservice_test/src/com/android/car/CarAudioExtFocusTest.java
@@ -13,10 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.car.test;
+package com.android.car;
import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.AUDIO_FOCUS;
-import static com.android.car.test.AudioTestUtils.doRequestFocus;
+import static com.android.car.AudioTestUtils.doRequestFocus;
import static java.lang.Integer.toHexString;
import android.car.Car;
@@ -227,7 +227,7 @@
public void testMediaNavFocus() throws Exception {
//music start
AudioFocusListener listenerMusic = new AudioFocusListener();
- int res = doRequestFocus(mAudioManager, listenerMusic,
+ int res = AudioTestUtils.doRequestFocus(mAudioManager, listenerMusic,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -249,7 +249,7 @@
setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
build();
- doRequestFocus(mAudioManager, listenerNav, navAttrib,
+ AudioTestUtils.doRequestFocus(mAudioManager, listenerNav, navAttrib,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
@@ -293,7 +293,7 @@
public void testMediaExternalMediaNavFocus() throws Exception {
// android music
AudioFocusListener listenerMusic = new AudioFocusListener();
- int res = doRequestFocus(mAudioManager, listenerMusic,
+ int res = AudioTestUtils.doRequestFocus(mAudioManager, listenerMusic,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -323,7 +323,7 @@
setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
build();
- doRequestFocus(mAudioManager, listenerNav, navAttrib,
+ AudioTestUtils.doRequestFocus(mAudioManager, listenerNav, navAttrib,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT_MAY_DUCK,
@@ -369,7 +369,7 @@
assertNotNull(carAudioManager);
AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage(
CarAudioManager.CAR_AUDIO_USAGE_RADIO);
- int res = doRequestFocus(mAudioManager, listenerRadio,
+ int res = AudioTestUtils.doRequestFocus(mAudioManager, listenerRadio,
radioAttributes, AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
@@ -389,7 +389,7 @@
AudioFocusListener listenerNav = new AudioFocusListener();
AudioAttributes extNavAttributes = mCarAudioManager.getAudioAttributesForExternalSource(
CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_EXT_NAV_GUIDANCE);
- res = doRequestFocus(mAudioManager, listenerNav,
+ res = AudioTestUtils.doRequestFocus(mAudioManager, listenerNav,
extNavAttributes, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
@@ -438,7 +438,7 @@
public void testMediaExternalNav() throws Exception {
// android music
AudioFocusListener listenerMusic = new AudioFocusListener();
- int res = doRequestFocus(mAudioManager, listenerMusic,
+ int res = AudioTestUtils.doRequestFocus(mAudioManager, listenerMusic,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -458,7 +458,7 @@
AudioFocusListener listenerNav = new AudioFocusListener();
AudioAttributes extNavAttributes = mCarAudioManager.getAudioAttributesForExternalSource(
CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_EXT_NAV_GUIDANCE);
- res = doRequestFocus(mAudioManager, listenerNav,
+ res = AudioTestUtils.doRequestFocus(mAudioManager, listenerNav,
extNavAttributes, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
@@ -514,7 +514,7 @@
AudioFocusListener listenerIntNav = new AudioFocusListener();
AudioAttributes intNavAttributes = mCarAudioManager.getAudioAttributesForCarUsage(
CarAudioManager.CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE);
- int res = doRequestFocus(mAudioManager, listenerIntNav, intNavAttributes,
+ int res = AudioTestUtils.doRequestFocus(mAudioManager, listenerIntNav, intNavAttributes,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
@@ -534,7 +534,7 @@
AudioFocusListener listenerExtNav = new AudioFocusListener();
AudioAttributes extNavAttributes = mCarAudioManager.getAudioAttributesForExternalSource(
CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_EXT_NAV_GUIDANCE);
- res = doRequestFocus(mAudioManager, listenerExtNav,
+ res = AudioTestUtils.doRequestFocus(mAudioManager, listenerExtNav,
extNavAttributes, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
@@ -582,7 +582,7 @@
public void testMediaExternalRadioNavMediaFocus() throws Exception {
// android music
AudioFocusListener listenerMusic = new AudioFocusListener();
- int res = doRequestFocus(mAudioManager, listenerMusic,
+ int res = AudioTestUtils.doRequestFocus(mAudioManager, listenerMusic,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -605,7 +605,7 @@
assertNotNull(carAudioManager);
AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage(
CarAudioManager.CAR_AUDIO_USAGE_RADIO);
- res = doRequestFocus(mAudioManager, listenerRadio,
+ res = AudioTestUtils.doRequestFocus(mAudioManager, listenerRadio,
radioAttributes, AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
@@ -627,7 +627,7 @@
setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
build();
- res = doRequestFocus(mAudioManager, listenerNav, navAttrib,
+ res = AudioTestUtils.doRequestFocus(mAudioManager, listenerNav, navAttrib,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN,
@@ -763,7 +763,7 @@
assertNotNull(carAudioManager);
AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage(mediaUsage);
Log.i(TAG, "request media Focus");
- int res = doRequestFocus(mAudioManager, listenerMedia,
+ int res = AudioTestUtils.doRequestFocus(mAudioManager, listenerMedia,
radioAttributes, AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
@@ -807,7 +807,7 @@
setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
build();
Log.i(TAG, "request nav Focus");
- res = doRequestFocus(mAudioManager, listenerNav, navAttrib,
+ res = AudioTestUtils.doRequestFocus(mAudioManager, listenerNav, navAttrib,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT_MAY_DUCK,
diff --git a/tests/carservice_test/src/com/android/car/test/CarAudioFocusSystemSoundTest.java b/tests/carservice_test/src/com/android/car/CarAudioFocusSystemSoundTest.java
similarity index 97%
rename from tests/carservice_test/src/com/android/car/test/CarAudioFocusSystemSoundTest.java
rename to tests/carservice_test/src/com/android/car/CarAudioFocusSystemSoundTest.java
index afafb28..5e0d2e7 100644
--- a/tests/carservice_test/src/com/android/car/test/CarAudioFocusSystemSoundTest.java
+++ b/tests/carservice_test/src/com/android/car/CarAudioFocusSystemSoundTest.java
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.car.test;
+package com.android.car;
import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.AUDIO_FOCUS;
import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.AUDIO_STREAM_STATE;
-import static com.android.car.test.AudioTestUtils.doRequestFocus;
+import static com.android.car.AudioTestUtils.doRequestFocus;
import com.google.android.collect.Lists;
@@ -137,7 +137,7 @@
assertNotNull(carAudioManager);
AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage(
CarAudioManager.CAR_AUDIO_USAGE_RADIO);
- int res = doRequestFocus(mAudioManager, listenerRadio,
+ int res = AudioTestUtils.doRequestFocus(mAudioManager, listenerRadio,
radioAttributes, AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
@@ -191,7 +191,7 @@
public void testMusicSystemSound() throws Exception {
// music start
AudioFocusListener listenerMusic = new AudioFocusListener();
- int res = doRequestFocus(mAudioManager, listenerMusic,
+ int res = AudioTestUtils.doRequestFocus(mAudioManager, listenerMusic,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -247,7 +247,7 @@
setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
build();
- int res = doRequestFocus(mAudioManager, listenerNav, navAttrib,
+ int res = AudioTestUtils.doRequestFocus(mAudioManager, listenerNav, navAttrib,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
diff --git a/tests/carservice_test/src/com/android/car/test/CarAudioFocusTest.java b/tests/carservice_test/src/com/android/car/CarAudioFocusTest.java
similarity index 96%
rename from tests/carservice_test/src/com/android/car/test/CarAudioFocusTest.java
rename to tests/carservice_test/src/com/android/car/CarAudioFocusTest.java
index 6f5da5f..982924f 100644
--- a/tests/carservice_test/src/com/android/car/test/CarAudioFocusTest.java
+++ b/tests/carservice_test/src/com/android/car/CarAudioFocusTest.java
@@ -13,10 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.car.test;
+package com.android.car;
import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.AUDIO_FOCUS;
-import static com.android.car.test.AudioTestUtils.doRequestFocus;
import android.car.Car;
import android.car.media.CarAudioManager;
@@ -136,7 +135,7 @@
public void testMediaNavFocus() throws Exception {
//music start
AudioFocusListener listenerMusic = new AudioFocusListener();
- int res = doRequestFocus(mAudioManager, listenerMusic,
+ int res = AudioTestUtils.doRequestFocus(mAudioManager, listenerMusic,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -156,7 +155,7 @@
setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
build();
- doRequestFocus(mAudioManager, listenerNav, navAttrib,
+ AudioTestUtils.doRequestFocus(mAudioManager, listenerNav, navAttrib,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN, request[0]);
@@ -194,7 +193,7 @@
public void testMediaExternalMediaNavFocus() throws Exception {
// android music
AudioFocusListener listenerMusic = new AudioFocusListener();
- int res = doRequestFocus(mAudioManager, listenerMusic,
+ int res = AudioTestUtils.doRequestFocus(mAudioManager, listenerMusic,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -222,7 +221,7 @@
setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
build();
- doRequestFocus(mAudioManager, listenerNav, navAttrib,
+ AudioTestUtils.doRequestFocus(mAudioManager, listenerNav, navAttrib,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT_MAY_DUCK,
@@ -259,7 +258,7 @@
public void testMediaExternalRadioNavMediaFocus() throws Exception {
// android music
AudioFocusListener listenerMusic = new AudioFocusListener();
- int res = doRequestFocus(mAudioManager, listenerMusic,
+ int res = AudioTestUtils.doRequestFocus(mAudioManager, listenerMusic,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -280,7 +279,7 @@
assertNotNull(carAudioManager);
AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage(
CarAudioManager.CAR_AUDIO_USAGE_RADIO);
- res = doRequestFocus(mAudioManager, listenerRadio,
+ res = AudioTestUtils.doRequestFocus(mAudioManager, listenerRadio,
radioAttributes, AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
@@ -300,7 +299,7 @@
setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
build();
- res = doRequestFocus(mAudioManager, listenerNav, navAttrib,
+ res = AudioTestUtils.doRequestFocus(mAudioManager, listenerNav, navAttrib,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN,
@@ -361,7 +360,7 @@
int context)
throws Exception {
AudioFocusListener lister = new AudioFocusListener();
- int res = doRequestFocus(mAudioManager, lister,
+ int res = AudioTestUtils.doRequestFocus(mAudioManager, lister,
streamType,
androidFocus);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -427,7 +426,7 @@
assertNotNull(carAudioManager);
AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage(mediaUsage);
Log.i(TAG, "request media Focus");
- int res = doRequestFocus(mAudioManager, listenerMedia,
+ int res = AudioTestUtils.doRequestFocus(mAudioManager, listenerMedia,
radioAttributes, AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
@@ -462,7 +461,7 @@
setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
build();
Log.i(TAG, "request nav Focus");
- res = doRequestFocus(mAudioManager, listenerNav, navAttrib,
+ res = AudioTestUtils.doRequestFocus(mAudioManager, listenerNav, navAttrib,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
assertEquals(VehicleAudioFocusRequest.REQUEST_GAIN_TRANSIENT_MAY_DUCK,
diff --git a/tests/carservice_test/src/com/android/car/test/CarAudioManagerTest.java b/tests/carservice_test/src/com/android/car/CarAudioManagerTest.java
similarity index 99%
rename from tests/carservice_test/src/com/android/car/test/CarAudioManagerTest.java
rename to tests/carservice_test/src/com/android/car/CarAudioManagerTest.java
index 07646d4..a66cc60 100644
--- a/tests/carservice_test/src/com/android/car/test/CarAudioManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/CarAudioManagerTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.car.test;
+package com.android.car;
import android.car.Car;
import android.car.media.CarAudioManager;
diff --git a/tests/carservice_test/src/com/android/car/test/CarCabinManagerTest.java b/tests/carservice_test/src/com/android/car/CarCabinManagerTest.java
similarity index 99%
rename from tests/carservice_test/src/com/android/car/test/CarCabinManagerTest.java
rename to tests/carservice_test/src/com/android/car/CarCabinManagerTest.java
index cef163e..58b784b 100644
--- a/tests/carservice_test/src/com/android/car/test/CarCabinManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/CarCabinManagerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.car.test;
+package com.android.car;
import android.car.Car;
import android.car.hardware.CarPropertyValue;
diff --git a/tests/carservice_test/src/com/android/car/test/CarDiagnosticConstantsTest.java b/tests/carservice_test/src/com/android/car/CarDiagnosticConstantsTest.java
similarity index 99%
rename from tests/carservice_test/src/com/android/car/test/CarDiagnosticConstantsTest.java
rename to tests/carservice_test/src/com/android/car/CarDiagnosticConstantsTest.java
index 2ed2658..33be912 100644
--- a/tests/carservice_test/src/com/android/car/test/CarDiagnosticConstantsTest.java
+++ b/tests/carservice_test/src/com/android/car/CarDiagnosticConstantsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.car.test;
+package com.android.car;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
diff --git a/tests/carservice_test/src/com/android/car/test/CarDiagnosticManagerTest.java b/tests/carservice_test/src/com/android/car/CarDiagnosticManagerTest.java
similarity index 98%
rename from tests/carservice_test/src/com/android/car/test/CarDiagnosticManagerTest.java
rename to tests/carservice_test/src/com/android/car/CarDiagnosticManagerTest.java
index b77a844..eb7ef57 100644
--- a/tests/carservice_test/src/com/android/car/test/CarDiagnosticManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/CarDiagnosticManagerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.car.test;
+package com.android.car;
import static java.lang.Integer.toHexString;
@@ -43,6 +43,7 @@
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
@@ -190,6 +191,7 @@
@Override
protected synchronized void configureMockedHal() {
java.util.Collection<Integer> numVendorSensors = Arrays.asList(0, 0);
+ java.util.Collection<Integer> selectiveClear = Collections.singletonList(1);
addProperty(VehicleProperty.OBD2_LIVE_FRAME, mLiveFrameEventBuilder.build())
.setConfigArray(numVendorSensors);
addProperty(
@@ -199,7 +201,8 @@
.setConfigArray(numVendorSensors);
addProperty(
VehicleProperty.OBD2_FREEZE_FRAME_CLEAR,
- mFreezeFrameProperties.mFreezeFrameClearHandler);
+ mFreezeFrameProperties.mFreezeFrameClearHandler)
+ .setConfigArray(selectiveClear);
}
@Override
@@ -732,6 +735,7 @@
assertTrue(mCarDiagnosticManager.isFreezeFrameNotificationSupported());
assertTrue(mCarDiagnosticManager.isGetFreezeFrameSupported());
assertTrue(mCarDiagnosticManager.isClearFreezeFramesSupported());
+ assertTrue(mCarDiagnosticManager.isSelectiveClearFreezeFramesSupported());
}
class Listener implements CarDiagnosticManager.OnDiagnosticEventListener {
diff --git a/tests/carservice_test/src/com/android/car/test/CarHvacManagerTest.java b/tests/carservice_test/src/com/android/car/CarHvacManagerTest.java
similarity index 99%
rename from tests/carservice_test/src/com/android/car/test/CarHvacManagerTest.java
rename to tests/carservice_test/src/com/android/car/CarHvacManagerTest.java
index 07f879e..9c89694 100644
--- a/tests/carservice_test/src/com/android/car/test/CarHvacManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/CarHvacManagerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.car.test;
+package com.android.car;
import android.car.Car;
import android.car.hardware.CarPropertyValue;
diff --git a/tests/carservice_test/src/com/android/car/test/CarInfoManagerTest.java b/tests/carservice_test/src/com/android/car/CarInfoManagerTest.java
similarity index 98%
rename from tests/carservice_test/src/com/android/car/test/CarInfoManagerTest.java
rename to tests/carservice_test/src/com/android/car/CarInfoManagerTest.java
index 2cdbbb0..fe32615 100644
--- a/tests/carservice_test/src/com/android/car/test/CarInfoManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/CarInfoManagerTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.car.test;
+package com.android.car;
import android.car.Car;
import android.car.CarInfoManager;
diff --git a/tests/carservice_test/src/com/android/car/test/CarPackageManagerTest.java b/tests/carservice_test/src/com/android/car/CarPackageManagerTest.java
similarity index 99%
rename from tests/carservice_test/src/com/android/car/test/CarPackageManagerTest.java
rename to tests/carservice_test/src/com/android/car/CarPackageManagerTest.java
index c455bc4..ffbf5a4 100644
--- a/tests/carservice_test/src/com/android/car/test/CarPackageManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/CarPackageManagerTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.car.test;
+package com.android.car;
import android.car.Car;
import android.car.CarNotConnectedException;
diff --git a/tests/carservice_test/src/com/android/car/test/CarPowerManagementTest.java b/tests/carservice_test/src/com/android/car/CarPowerManagementTest.java
similarity index 83%
rename from tests/carservice_test/src/com/android/car/test/CarPowerManagementTest.java
rename to tests/carservice_test/src/com/android/car/CarPowerManagementTest.java
index d5e5bf8..b0ac2e7 100644
--- a/tests/carservice_test/src/com/android/car/test/CarPowerManagementTest.java
+++ b/tests/carservice_test/src/com/android/car/CarPowerManagementTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.car.test;
+package com.android.car;
import android.hardware.automotive.vehicle.V2_0.VehicleApPowerBootupReason;
import android.hardware.automotive.vehicle.V2_0.VehicleApPowerSetState;
@@ -26,6 +26,8 @@
import android.os.SystemClock;
import android.test.suitebuilder.annotation.MediumTest;
+import com.android.car.systeminterface.DisplayInterface;
+import com.android.car.systeminterface.SystemInterface;
import com.google.android.collect.Lists;
import com.android.car.vehiclehal.VehiclePropValueBuilder;
@@ -40,6 +42,13 @@
public class CarPowerManagementTest extends MockedCarTestBase {
private final PowerStatePropertyHandler mPowerStateHandler = new PowerStatePropertyHandler();
+ private final MockDisplayInterface mMockDisplayInterface = new MockDisplayInterface();
+
+ @Override
+ protected synchronized SystemInterface.Builder getSystemInterfaceBuilder() {
+ SystemInterface.Builder builder = super.getSystemInterfaceBuilder();
+ return builder.withDisplayInterface(mMockDisplayInterface);
+ }
private void setupPowerPropertyAndStart(boolean allowSleep) {
addProperty(VehicleProperty.AP_POWER_STATE, mPowerStateHandler)
@@ -70,9 +79,9 @@
assertBootComplete();
for (int i = 0; i < 2; i++) {
mPowerStateHandler.sendPowerState(VehicleApPowerState.ON_DISP_OFF, 0);
- waitForFakeDisplayState(false);
+ mMockDisplayInterface.waitForDisplayState(false);
mPowerStateHandler.sendPowerState(VehicleApPowerState.ON_FULL, 0);
- waitForFakeDisplayState(true);
+ mMockDisplayInterface.waitForDisplayState(true);
}
}
@@ -107,6 +116,33 @@
assertEquals(0, first[1]);
}
+ private final class MockDisplayInterface implements DisplayInterface {
+ private boolean mDisplayOn = true;
+ private final Semaphore mDisplayStateWait = new Semaphore(0);
+
+ @Override
+ public synchronized void setDisplayState(boolean on) {
+ mDisplayOn = on;
+ mDisplayStateWait.release();
+ }
+
+ boolean waitForDisplayState(boolean expectedState)
+ throws Exception {
+ if (expectedState == mDisplayOn) {
+ return true;
+ }
+ mDisplayStateWait.tryAcquire(MockedCarTestBase.SHORT_WAIT_TIMEOUT_MS,
+ TimeUnit.MILLISECONDS);
+ return expectedState == mDisplayOn;
+ }
+
+ @Override
+ public void startDisplayStateMonitoring(CarPowerManagementService service) {}
+
+ @Override
+ public void stopDisplayStateMonitoring() {}
+ }
+
private class PowerStatePropertyHandler implements VehicleHalPropertyHandler {
private int mPowerState = VehicleApPowerState.ON_FULL;
diff --git a/tests/carservice_test/src/com/android/car/test/CarProjectionManagerTest.java b/tests/carservice_test/src/com/android/car/CarProjectionManagerTest.java
similarity index 99%
rename from tests/carservice_test/src/com/android/car/test/CarProjectionManagerTest.java
rename to tests/carservice_test/src/com/android/car/CarProjectionManagerTest.java
index adf288c..773a296 100644
--- a/tests/carservice_test/src/com/android/car/test/CarProjectionManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/CarProjectionManagerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.car.test;
+package com.android.car;
import android.car.Car;
import android.car.CarProjectionManager;
diff --git a/tests/carservice_test/src/com/android/car/test/CarRadioManagerTest.java b/tests/carservice_test/src/com/android/car/CarRadioManagerTest.java
similarity index 99%
rename from tests/carservice_test/src/com/android/car/test/CarRadioManagerTest.java
rename to tests/carservice_test/src/com/android/car/CarRadioManagerTest.java
index 169a370..7288259 100644
--- a/tests/carservice_test/src/com/android/car/test/CarRadioManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/CarRadioManagerTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.car.test;
+package com.android.car;
import android.car.Car;
import android.car.hardware.radio.CarRadioEvent;
diff --git a/tests/carservice_test/src/com/android/car/test/CarSensorManagerTest.java b/tests/carservice_test/src/com/android/car/CarSensorManagerTest.java
similarity index 99%
rename from tests/carservice_test/src/com/android/car/test/CarSensorManagerTest.java
rename to tests/carservice_test/src/com/android/car/CarSensorManagerTest.java
index daddfdb..00120a3 100644
--- a/tests/carservice_test/src/com/android/car/test/CarSensorManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/CarSensorManagerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.car.test;
+package com.android.car;
import android.car.Car;
import android.car.CarNotConnectedException;
diff --git a/tests/carservice_test/src/com/android/car/CarStorageMonitoringTest.java b/tests/carservice_test/src/com/android/car/CarStorageMonitoringTest.java
new file mode 100644
index 0000000..24d75a5
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/CarStorageMonitoringTest.java
@@ -0,0 +1,435 @@
+/*
+ * 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.NonNull;
+import android.annotation.Nullable;
+import android.car.Car;
+import android.car.storagemonitoring.CarStorageMonitoringManager;
+import android.car.storagemonitoring.UidIoStats;
+import android.car.storagemonitoring.UidIoStatsRecord;
+import android.car.storagemonitoring.WearEstimate;
+import android.car.storagemonitoring.WearEstimateChange;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.JsonWriter;
+import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
+import com.android.car.storagemonitoring.UidIoStatsProvider;
+import com.android.car.storagemonitoring.WearEstimateRecord;
+import com.android.car.storagemonitoring.WearHistory;
+import com.android.car.storagemonitoring.WearInformation;
+import com.android.car.storagemonitoring.WearInformationProvider;
+import com.android.car.systeminterface.StorageMonitoringInterface;
+import com.android.car.systeminterface.SystemInterface;
+import com.android.car.systeminterface.SystemStateInterface;
+import com.android.car.systeminterface.TimeInterface;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Test the public entry points for the CarStorageMonitoringManager */
+@MediumTest
+public class CarStorageMonitoringTest extends MockedCarTestBase {
+ private static final String TAG = CarStorageMonitoringTest.class.getSimpleName();
+
+ private static final WearInformation DEFAULT_WEAR_INFORMATION =
+ new WearInformation(30, 0, WearInformation.PRE_EOL_INFO_NORMAL);
+
+ private static final class TestData {
+ static final TestData DEFAULT = new TestData(0, DEFAULT_WEAR_INFORMATION, null, null);
+
+ final long uptime;
+ @NonNull
+ final WearInformation wearInformation;
+ @Nullable
+ final WearHistory wearHistory;
+ @NonNull
+ final UidIoStatsRecord[] ioStats;
+
+ TestData(long uptime,
+ @Nullable WearInformation wearInformation,
+ @Nullable WearHistory wearHistory,
+ @Nullable UidIoStatsRecord[] ioStats) {
+ if (wearInformation == null) wearInformation = DEFAULT_WEAR_INFORMATION;
+ if (ioStats == null) ioStats = new UidIoStatsRecord[0];
+ this.uptime = uptime;
+ this.wearInformation = wearInformation;
+ this.wearHistory = wearHistory;
+ this.ioStats = ioStats;
+ }
+ }
+
+ private static final Map<String, TestData> PER_TEST_DATA =
+ new HashMap<String, TestData>() {{
+ put("testReadWearHistory",
+ new TestData(6500, DEFAULT_WEAR_INFORMATION,
+ WearHistory.fromRecords(
+ WearEstimateRecord.Builder.newBuilder()
+ .fromWearEstimate(WearEstimate.UNKNOWN_ESTIMATE)
+ .toWearEstimate(new WearEstimate(10, 0))
+ .atUptime(1000)
+ .atTimestamp(Instant.ofEpochMilli(5000)).build(),
+ WearEstimateRecord.Builder.newBuilder()
+ .fromWearEstimate(new WearEstimate(10, 0))
+ .toWearEstimate(new WearEstimate(20, 0))
+ .atUptime(4000)
+ .atTimestamp(Instant.ofEpochMilli(12000)).build(),
+ WearEstimateRecord.Builder.newBuilder()
+ .fromWearEstimate(new WearEstimate(20, 0))
+ .toWearEstimate(new WearEstimate(30, 0))
+ .atUptime(6500)
+ .atTimestamp(Instant.ofEpochMilli(17000)).build()), null));
+
+ put("testNotAcceptableWearEvent",
+ new TestData(2520006499L,
+ new WearInformation(40, 0, WearInformation.PRE_EOL_INFO_NORMAL),
+ WearHistory.fromRecords(
+ WearEstimateRecord.Builder.newBuilder()
+ .fromWearEstimate(WearEstimate.UNKNOWN_ESTIMATE)
+ .toWearEstimate(new WearEstimate(10, 0))
+ .atUptime(1000)
+ .atTimestamp(Instant.ofEpochMilli(5000)).build(),
+ WearEstimateRecord.Builder.newBuilder()
+ .fromWearEstimate(new WearEstimate(10, 0))
+ .toWearEstimate(new WearEstimate(20, 0))
+ .atUptime(4000)
+ .atTimestamp(Instant.ofEpochMilli(12000)).build(),
+ WearEstimateRecord.Builder.newBuilder()
+ .fromWearEstimate(new WearEstimate(20, 0))
+ .toWearEstimate(new WearEstimate(30, 0))
+ .atUptime(6500)
+ .atTimestamp(Instant.ofEpochMilli(17000)).build()), null));
+
+ put("testAcceptableWearEvent",
+ new TestData(2520006501L,
+ new WearInformation(40, 0, WearInformation.PRE_EOL_INFO_NORMAL),
+ WearHistory.fromRecords(
+ WearEstimateRecord.Builder.newBuilder()
+ .fromWearEstimate(WearEstimate.UNKNOWN_ESTIMATE)
+ .toWearEstimate(new WearEstimate(10, 0))
+ .atUptime(1000)
+ .atTimestamp(Instant.ofEpochMilli(5000)).build(),
+ WearEstimateRecord.Builder.newBuilder()
+ .fromWearEstimate(new WearEstimate(10, 0))
+ .toWearEstimate(new WearEstimate(20, 0))
+ .atUptime(4000)
+ .atTimestamp(Instant.ofEpochMilli(12000)).build(),
+ WearEstimateRecord.Builder.newBuilder()
+ .fromWearEstimate(new WearEstimate(20, 0))
+ .toWearEstimate(new WearEstimate(30, 0))
+ .atUptime(6500)
+ .atTimestamp(Instant.ofEpochMilli(17000)).build()), null));
+
+ put("testBootIoStats",
+ new TestData(1000L,
+ new WearInformation(0, 0, WearInformation.PRE_EOL_INFO_NORMAL),
+ null,
+ new UidIoStatsRecord[]{
+ new UidIoStatsRecord(0, 5000, 6000, 3000, 1000, 1,
+ 0, 0, 0, 0, 0),
+ new UidIoStatsRecord(1000, 200, 5000, 0, 4000, 0,
+ 1000, 0, 500, 0, 0)}));
+
+ put("testAggregateIoStats",
+ new TestData(1000L,
+ new WearInformation(0, 0, WearInformation.PRE_EOL_INFO_NORMAL),
+ null,
+ new UidIoStatsRecord[]{
+ new UidIoStatsRecord(0, 5000, 6000, 3000, 1000, 1,
+ 0, 0, 0, 0, 0),
+ new UidIoStatsRecord(1000, 200, 5000, 0, 4000, 0,
+ 1000, 0, 500, 0, 0)}));
+
+ }};
+
+ private final MockSystemStateInterface mMockSystemStateInterface =
+ new MockSystemStateInterface();
+ private final MockStorageMonitoringInterface mMockStorageMonitoringInterface =
+ new MockStorageMonitoringInterface();
+ private final MockTimeInterface mMockTimeInterface =
+ new MockTimeInterface();
+
+ private CarStorageMonitoringManager mCarStorageMonitoringManager;
+
+ @Override
+ protected synchronized SystemInterface.Builder getSystemInterfaceBuilder() {
+ SystemInterface.Builder builder = super.getSystemInterfaceBuilder();
+ return builder.withSystemStateInterface(mMockSystemStateInterface)
+ .withStorageMonitoringInterface(mMockStorageMonitoringInterface)
+ .withTimeInterface(mMockTimeInterface);
+ }
+
+ @Override
+ protected synchronized void configureFakeSystemInterface() {
+ try {
+ final String testName = getName();
+ final TestData wearData = PER_TEST_DATA.getOrDefault(testName, TestData.DEFAULT);
+ final WearHistory wearHistory = wearData.wearHistory;
+
+ mMockStorageMonitoringInterface.setWearInformation(wearData.wearInformation);
+
+ if (wearHistory != null) {
+ File wearHistoryFile = new File(getFakeSystemInterface().getFilesDir(),
+ CarStorageMonitoringService.WEAR_INFO_FILENAME);
+ try (JsonWriter jsonWriter = new JsonWriter(new FileWriter(wearHistoryFile))) {
+ wearHistory.writeToJson(jsonWriter);
+ }
+ }
+
+ if (wearData.uptime > 0) {
+ File uptimeFile = new File(getFakeSystemInterface().getFilesDir(),
+ CarStorageMonitoringService.UPTIME_TRACKER_FILENAME);
+ try (JsonWriter jsonWriter = new JsonWriter(new FileWriter(uptimeFile))) {
+ jsonWriter.beginObject();
+ jsonWriter.name("uptime").value(wearData.uptime);
+ jsonWriter.endObject();
+ }
+ }
+
+ Arrays.stream(wearData.ioStats).forEach(
+ mMockStorageMonitoringInterface::addIoStatsRecord);
+
+ } catch (IOException e) {
+ Log.e(TAG, "failed to configure fake system interface", e);
+ fail("failed to configure fake system interface instance");
+ }
+
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mMockSystemStateInterface.executeBootCompletedActions();
+
+ mCarStorageMonitoringManager =
+ (CarStorageMonitoringManager) getCar().getCarManager(Car.STORAGE_MONITORING_SERVICE);
+ }
+
+ public void testReadPreEolInformation() throws Exception {
+ assertEquals(DEFAULT_WEAR_INFORMATION.preEolInfo,
+ mCarStorageMonitoringManager.getPreEolIndicatorStatus());
+ }
+
+ public void testReadWearEstimate() throws Exception {
+ final WearEstimate wearEstimate = mCarStorageMonitoringManager.getWearEstimate();
+
+ assertNotNull(wearEstimate);
+ assertEquals(DEFAULT_WEAR_INFORMATION.lifetimeEstimateA, wearEstimate.typeA);
+ assertEquals(DEFAULT_WEAR_INFORMATION.lifetimeEstimateB, wearEstimate.typeB);
+ }
+
+ public void testReadWearHistory() throws Exception {
+ final List<WearEstimateChange> wearEstimateChanges =
+ mCarStorageMonitoringManager.getWearEstimateHistory();
+
+ assertNotNull(wearEstimateChanges);
+ assertFalse(wearEstimateChanges.isEmpty());
+
+ final WearHistory expectedWearHistory = PER_TEST_DATA.get(getName()).wearHistory;
+
+ assertEquals(expectedWearHistory.size(), wearEstimateChanges.size());
+ for (int i = 0; i < wearEstimateChanges.size(); ++i) {
+ final WearEstimateRecord expected = expectedWearHistory.get(i);
+ final WearEstimateChange actual = wearEstimateChanges.get(i);
+
+ assertTrue(expected.isSameAs(actual));
+ }
+ }
+
+ private void checkLastWearEvent(boolean isAcceptable) throws Exception {
+ final List<WearEstimateChange> wearEstimateChanges =
+ mCarStorageMonitoringManager.getWearEstimateHistory();
+
+ assertNotNull(wearEstimateChanges);
+ assertFalse(wearEstimateChanges.isEmpty());
+
+ final TestData wearData = PER_TEST_DATA.get(getName());
+
+ final WearInformation expectedCurrentWear = wearData.wearInformation;
+ final WearEstimate expectedPreviousWear = wearData.wearHistory.getLast().getNewWearEstimate();
+
+ final WearEstimateChange actualCurrentWear =
+ wearEstimateChanges.get(wearEstimateChanges.size() - 1);
+
+ assertEquals(isAcceptable, actualCurrentWear.isAcceptableDegradation);
+ assertEquals(expectedCurrentWear.toWearEstimate(), actualCurrentWear.newEstimate);
+ assertEquals(expectedPreviousWear, actualCurrentWear.oldEstimate);
+ }
+
+ public void testNotAcceptableWearEvent() throws Exception {
+ checkLastWearEvent(false);
+ }
+
+ public void testAcceptableWearEvent() throws Exception {
+ checkLastWearEvent(true);
+ }
+
+ public void testBootIoStats() throws Exception {
+ final List<UidIoStats> bootIoStats =
+ mCarStorageMonitoringManager.getBootIoStats();
+
+ assertNotNull(bootIoStats);
+ assertFalse(bootIoStats.isEmpty());
+
+ final UidIoStatsRecord[] bootIoRecords = PER_TEST_DATA.get(getName()).ioStats;
+
+ bootIoStats.forEach(uidIoStats -> assertTrue(Arrays.stream(bootIoRecords).anyMatch(
+ ioRecord -> uidIoStats.representsSameMetrics(ioRecord))));
+ }
+
+ public void testAggregateIoStats() throws Exception {
+ UidIoStatsRecord oldRecord1000 = mMockStorageMonitoringInterface.getIoStatsRecord(1000);
+
+ UidIoStatsRecord newRecord1000 = new UidIoStatsRecord(1000,
+ oldRecord1000.foreground_rchar,
+ oldRecord1000.foreground_wchar + 50,
+ oldRecord1000.foreground_read_bytes,
+ oldRecord1000.foreground_write_bytes + 100,
+ oldRecord1000.foreground_fsync + 1,
+ oldRecord1000.background_rchar,
+ oldRecord1000.background_wchar,
+ oldRecord1000.background_read_bytes,
+ oldRecord1000.background_write_bytes,
+ oldRecord1000.background_fsync);
+
+ mMockStorageMonitoringInterface.addIoStatsRecord(newRecord1000);
+
+ UidIoStatsRecord record2000 = new UidIoStatsRecord(2000,
+ 1024,
+ 2048,
+ 0,
+ 1024,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0);
+
+ mMockStorageMonitoringInterface.addIoStatsRecord(record2000);
+
+ mMockTimeInterface.tick();
+
+ List<UidIoStats> aggregateIoStats = mCarStorageMonitoringManager.getAggregateIoStats();
+
+ assertNotNull(aggregateIoStats);
+ assertFalse(aggregateIoStats.isEmpty());
+
+ aggregateIoStats.forEach(serviceIoStat -> {
+ UidIoStatsRecord mockIoStat = mMockStorageMonitoringInterface.getIoStatsRecord(
+ serviceIoStat.uid);
+
+ assertNotNull(mockIoStat);
+
+ assertTrue(serviceIoStat.representsSameMetrics(mockIoStat));
+ });
+ }
+
+ static final class MockStorageMonitoringInterface implements StorageMonitoringInterface,
+ WearInformationProvider {
+ private WearInformation mWearInformation = null;
+ private SparseArray<UidIoStatsRecord> mIoStats = new SparseArray<>();
+ private UidIoStatsProvider mIoStatsProvider = () -> mIoStats;
+
+ void setWearInformation(WearInformation wearInformation) {
+ mWearInformation = wearInformation;
+ }
+
+ void addIoStatsRecord(UidIoStatsRecord record) {
+ mIoStats.append(record.uid, record);
+ }
+
+ UidIoStatsRecord getIoStatsRecord(int uid) {
+ return mIoStats.get(uid);
+ }
+
+ void deleteIoStatsRecord(int uid) {
+ mIoStats.delete(uid);
+ }
+
+ @Override
+ public WearInformation load() {
+ return mWearInformation;
+ }
+
+ @Override
+ public WearInformationProvider[] getFlashWearInformationProviders() {
+ return new WearInformationProvider[] {this};
+ }
+
+ @Override
+ public UidIoStatsProvider getUidIoStatsProvider() {
+ return mIoStatsProvider;
+ }
+ }
+
+ static final class MockTimeInterface implements TimeInterface {
+ private final List<Pair<Runnable, Long>> mActionsList = new ArrayList<>();
+
+ @Override
+ public long getUptime(boolean includeDeepSleepTime) {
+ return 0;
+ }
+
+ @Override
+ public void scheduleAction(Runnable r, long delayMs) {
+ mActionsList.add(Pair.create(r, delayMs));
+ mActionsList.sort(Comparator.comparing(d -> d.second));
+ }
+
+ @Override
+ public void cancelAllActions() {
+ mActionsList.clear();
+ }
+
+ void tick() {
+ mActionsList.forEach(pair -> pair.first.run());
+ }
+ }
+
+ static final class MockSystemStateInterface implements SystemStateInterface {
+ private final List<Pair<Runnable, Duration>> mActionsList = new ArrayList<>();
+
+ @Override
+ public void shutdown() {}
+
+ @Override
+ public void enterDeepSleep(int wakeupTimeSec) {}
+
+ @Override
+ public void scheduleActionForBootCompleted(Runnable action, Duration delay) {
+ mActionsList.add(Pair.create(action, delay));
+ mActionsList.sort(Comparator.comparing(d -> d.second));
+ }
+
+ void executeBootCompletedActions() {
+ for (Pair<Runnable, Duration> action : mActionsList) {
+ action.first.run();
+ }
+ }
+ }
+}
diff --git a/tests/carservice_test/src/com/android/car/test/CarVendorExtensionManagerTest.java b/tests/carservice_test/src/com/android/car/CarVendorExtensionManagerTest.java
similarity index 98%
rename from tests/carservice_test/src/com/android/car/test/CarVendorExtensionManagerTest.java
rename to tests/carservice_test/src/com/android/car/CarVendorExtensionManagerTest.java
index fa33e56..942649d 100644
--- a/tests/carservice_test/src/com/android/car/test/CarVendorExtensionManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/CarVendorExtensionManagerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.car.test;
+package com.android.car;
import static com.android.car.CarServiceUtils.toByteArray;
@@ -246,7 +246,7 @@
@Override
public synchronized void get(VehiclePropValue requestedPropValue, getCallback cb) {
if (!isVendorProperty(requestedPropValue.prop)) {
- cb.onValues(StatusCode.INVALID_ARG, null);
+ super.get(requestedPropValue, cb);
return;
}
VehiclePropValue result = new VehiclePropValue();
diff --git a/tests/carservice_test/src/com/android/car/test/CarVolumeServiceTest.java b/tests/carservice_test/src/com/android/car/CarVolumeServiceTest.java
similarity index 90%
rename from tests/carservice_test/src/com/android/car/test/CarVolumeServiceTest.java
rename to tests/carservice_test/src/com/android/car/CarVolumeServiceTest.java
index 2e92a59..0d9a607 100644
--- a/tests/carservice_test/src/com/android/car/test/CarVolumeServiceTest.java
+++ b/tests/carservice_test/src/com/android/car/CarVolumeServiceTest.java
@@ -13,9 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.car.test;
-
-import static com.android.car.test.AudioTestUtils.doRequestFocus;
+package com.android.car;
import android.car.Car;
import android.car.CarNotConnectedException;
@@ -40,13 +38,12 @@
import android.util.SparseIntArray;
import android.view.KeyEvent;
-import com.android.car.VolumeUtils;
+import com.google.android.collect.Lists;
+
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.List;
@@ -54,7 +51,6 @@
public class CarVolumeServiceTest extends MockedCarTestBase {
private static final String TAG = CarVolumeServiceTest.class.getSimpleName();
- private static final int MIN_VOL = 1;
private static final int MAX_VOL = 20;
private static final long TIMEOUT_MS = 3000;
private static final long POLL_INTERVAL_MS = 50;
@@ -101,7 +97,7 @@
}
}
SingleChannelVolumeHandler handler =
- startVolumeEmulation(supportedAudioContext, maxs);
+ startVolumeEmulation(true, supportedAudioContext, maxs);
mCarAudioManager = (CarAudioManager) getCar().getCarManager(Car.AUDIO_SERVICE);
return handler;
}
@@ -114,7 +110,7 @@
// give focus to music, now current context becomes VehicleAudioContextFlag.MUSIC_FLAG
CarAudioFocusTest.AudioFocusListener listenerMusic =
new CarAudioFocusTest.AudioFocusListener();
- int res = doRequestFocus(mAudioManager, listenerMusic,
+ int res = AudioTestUtils.doRequestFocus(mAudioManager, listenerMusic,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -138,7 +134,7 @@
}
}
- public void testVolumeSet() throws Exception {
+ public void testVolumeSet() {
try {
setupExternalVolumeEmulation(false);
int callVol = 10;
@@ -154,8 +150,27 @@
volumeVerificationPoll(createStreamVolPair(AudioManager.STREAM_MUSIC, MAX_VOL),
createStreamVolPair(AudioManager.STREAM_VOICE_CALL, callVol));
- } catch (CarNotConnectedException e) {
- fail("Car not connected");
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+ }
+
+ public void testMultipleVolumeControllers() {
+ try {
+ startVolumeEmulation(false, 0, new ArrayList<>());
+ mCarAudioManager = (CarAudioManager) getCar().getCarManager(Car.AUDIO_SERVICE);
+ mCarAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0);
+ VolumeController volumeController1 = new VolumeController();
+ VolumeController volumeController2 = new VolumeController();
+ mCarAudioManager.setVolumeController(volumeController1);
+ mCarAudioManager.setVolumeController(volumeController2);
+
+ mCarAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
+ 1, AudioManager.FLAG_SHOW_UI);
+ volumeChangeVerificationPoll(AudioManager.STREAM_MUSIC, true, volumeController1);
+ volumeChangeVerificationPoll(AudioManager.STREAM_MUSIC, true, volumeController2);
+ } catch (Exception e) {
+ fail(e.getMessage());
}
}
@@ -168,7 +183,7 @@
// first give focus to system sound
CarAudioFocusTest.AudioFocusListener listenerMusic =
new CarAudioFocusTest.AudioFocusListener();
- int res = doRequestFocus(mAudioManager, listenerMusic,
+ int res = AudioTestUtils.doRequestFocus(mAudioManager, listenerMusic,
AudioManager.STREAM_SYSTEM,
AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -184,7 +199,7 @@
AudioAttributes callAttrib = (new AudioAttributes.Builder()).
setUsage(AudioAttributes.USAGE_ALARM).
build();
- res = doRequestFocus(mAudioManager, listenerAlarm, callAttrib,
+ res = AudioTestUtils.doRequestFocus(mAudioManager, listenerAlarm, callAttrib,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
@@ -207,7 +222,7 @@
}
}
- public void testVolumeKeys() throws Exception {
+ public void testVolumeKeys() {
try {
setupExternalVolumeEmulation(false);
int musicVol = 10;
@@ -217,7 +232,7 @@
CarAudioFocusTest.AudioFocusListener listenerMusic =
new CarAudioFocusTest.AudioFocusListener();
- int res = doRequestFocus(mAudioManager, listenerMusic,
+ int res = AudioTestUtils.doRequestFocus(mAudioManager, listenerMusic,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -239,7 +254,7 @@
AudioAttributes callAttrib = (new AudioAttributes.Builder()).
setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION).
build();
- doRequestFocus(mAudioManager, listenerCall, callAttrib,
+ AudioTestUtils.doRequestFocus(mAudioManager, listenerCall, callAttrib,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
mAudioFocusPropertyHandler.sendAudioFocusState(
@@ -250,7 +265,7 @@
callVol++;
volumeVerificationPoll(createStreamVolPair(AudioManager.STREAM_MUSIC, musicVol),
createStreamVolPair(AudioManager.STREAM_VOICE_CALL, callVol));
- } catch (CarNotConnectedException | InterruptedException e) {
+ } catch (Exception e) {
fail(e.toString());
}
}
@@ -411,8 +426,8 @@
}
};
- private SingleChannelVolumeHandler startVolumeEmulation(int supportedAudioVolumeContext,
- List<Integer> maxs) {
+ private SingleChannelVolumeHandler startVolumeEmulation(boolean supportExternalVolume,
+ int supportedAudioVolumeContext, List<Integer> maxs) {
SingleChannelVolumeHandler singleChannelVolumeHandler =
new SingleChannelVolumeHandler(maxs);
int zones = (1<<maxs.size()) - 1;
@@ -425,9 +440,11 @@
0 /* reserved */);
audioVolumeConfigArray.addAll(maxs);
- addProperty(VehicleProperty.AUDIO_VOLUME, singleChannelVolumeHandler)
- .setConfigArray(audioVolumeConfigArray)
- .setSupportedAreas(zones);
+ if (supportExternalVolume) {
+ addProperty(VehicleProperty.AUDIO_VOLUME, singleChannelVolumeHandler)
+ .setConfigArray(audioVolumeConfigArray)
+ .setSupportedAreas(zones);
+ }
addProperty(VehicleProperty.HW_KEY_INPUT, mHWKeyHandler)
.setAccess(VehiclePropertyAccess.READ);
diff --git a/tests/carservice_test/src/com/android/car/test/GarageModeTest.java b/tests/carservice_test/src/com/android/car/GarageModeTest.java
similarity index 96%
rename from tests/carservice_test/src/com/android/car/test/GarageModeTest.java
rename to tests/carservice_test/src/com/android/car/GarageModeTest.java
index 19ccb6e..06abdc8 100644
--- a/tests/carservice_test/src/com/android/car/test/GarageModeTest.java
+++ b/tests/carservice_test/src/com/android/car/GarageModeTest.java
@@ -13,17 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.car.test;
+package com.android.car;
import android.car.settings.CarSettings;
import android.content.Context;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
-import com.android.car.CarPowerManagementService;
-import com.android.car.DeviceIdleControllerWrapper;
-import com.android.car.GarageModeService;
-
@MediumTest
public class GarageModeTest extends AndroidTestCase {
diff --git a/tests/carservice_test/src/com/android/car/test/MockedCarTestBase.java b/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
similarity index 64%
rename from tests/carservice_test/src/com/android/car/test/MockedCarTestBase.java
rename to tests/carservice_test/src/com/android/car/MockedCarTestBase.java
index a060496..e6ed401 100644
--- a/tests/carservice_test/src/com/android/car/test/MockedCarTestBase.java
+++ b/tests/carservice_test/src/com/android/car/MockedCarTestBase.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.car.test;
+package com.android.car;
import android.car.test.CarTestManager;
import android.car.test.CarTestManagerBinderWrapper;
@@ -21,7 +21,9 @@
import android.content.Context;
import android.content.ServiceConnection;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.hardware.automotive.vehicle.V2_0.VehicleDrivingStatus;
import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
import android.os.Binder;
@@ -31,20 +33,31 @@
import android.test.AndroidTestCase;
import android.util.Log;
-import com.android.car.CarPowerManagementService;
-import com.android.car.ICarImpl;
-import com.android.car.SystemInterface;
+import android.util.SparseArray;
+
+import com.android.car.systeminterface.DisplayInterface;
+import com.android.car.systeminterface.IOInterface;
+import com.android.car.systeminterface.StorageMonitoringInterface;
+import com.android.car.systeminterface.SystemInterface;
+import com.android.car.systeminterface.SystemInterface.Builder;
+import com.android.car.systeminterface.SystemStateInterface;
+import com.android.car.systeminterface.TimeInterface;
+import com.android.car.systeminterface.WakeLockInterface;
+import com.android.car.test.utils.TemporaryDirectory;
+import com.android.car.vehiclehal.VehiclePropValueBuilder;
import com.android.car.vehiclehal.test.MockedVehicleHal;
import com.android.car.vehiclehal.test.MockedVehicleHal.DefaultPropertyHandler;
import com.android.car.vehiclehal.test.MockedVehicleHal.StaticPropertyHandler;
import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
import com.android.car.vehiclehal.test.VehiclePropConfigBuilder;
+import java.io.File;
+import java.io.IOException;
+import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
/**
* Base class for testing with mocked vehicle HAL (=car).
@@ -59,11 +72,16 @@
private android.car.Car mCar;
private ICarImpl mCarImpl;
private MockedVehicleHal mMockedVehicleHal;
- private FakeSystemInterface mFakeSystemInterface;
+ private SystemInterface mFakeSystemInterface;
+ private final MockIOInterface mMockIOInterface = new MockIOInterface();
private final Semaphore mWaitForMain = new Semaphore(0);
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+ private final Map<VehiclePropConfigBuilder, VehicleHalPropertyHandler> mHalConfig =
+ new HashMap<>();
+ private final SparseArray<VehiclePropConfigBuilder> mPropToConfigBuilder = new SparseArray<>();
+
private static final IBinder mCarServiceToken = new Binder();
private static boolean mRealCarServiceReleased = false;
@@ -75,9 +93,25 @@
return mMockedVehicleHal;
}
+ protected synchronized SystemInterface getFakeSystemInterface() {
+ return mFakeSystemInterface;
+ }
+
protected synchronized void configureMockedHal() {
}
+ protected synchronized SystemInterface.Builder getSystemInterfaceBuilder() {
+ return Builder.newSystemInterface()
+ .withSystemStateInterface(new MockSystemStateInterface())
+ .withDisplayInterface(new MockDisplayInterface())
+ .withIOInterface(mMockIOInterface)
+ .withStorageMonitoringInterface(new MockStorageMonitoringInterface())
+ .withTimeInterface(new MockTimeInterface())
+ .withWakeLockInterface(new MockWakeLockInterface());
+ }
+
+ protected synchronized void configureFakeSystemInterface() {}
+
@Override
protected synchronized void setUp() throws Exception {
super.setUp();
@@ -85,9 +119,15 @@
releaseRealCarService(getContext());
mMockedVehicleHal = createMockedVehicleHal();
+ addProperty(VehicleProperty.DRIVING_STATUS,
+ VehiclePropValueBuilder.newBuilder(VehicleProperty.DRIVING_STATUS)
+ .addIntValue(VehicleDrivingStatus.UNRESTRICTED)
+ .build());
configureMockedHal();
- mFakeSystemInterface = new FakeSystemInterface();
+ mFakeSystemInterface = getSystemInterfaceBuilder().build();
+ configureFakeSystemInterface();
+
Context context = getCarServiceContext();
mCarImpl = new ICarImpl(context, mMockedVehicleHal, mFakeSystemInterface,
null /* error notifier */);
@@ -103,6 +143,8 @@
mCar.disconnect();
mCarImpl.release();
+
+ mMockIOInterface.tearDown();
}
protected Context getCarServiceContext() throws NameNotFoundException {
@@ -127,26 +169,23 @@
mCarImpl.init();
}
- private final Map<VehiclePropConfigBuilder, VehicleHalPropertyHandler> mHalConfig =
- new HashMap<>();
-
protected synchronized VehiclePropConfigBuilder addProperty(int propertyId,
VehicleHalPropertyHandler propertyHandler) {
VehiclePropConfigBuilder builder = VehiclePropConfigBuilder.newBuilder(propertyId);
- mHalConfig.put(builder, propertyHandler);
+ setConfigBuilder(builder, propertyHandler);
return builder;
}
protected synchronized VehiclePropConfigBuilder addProperty(int propertyId) {
VehiclePropConfigBuilder builder = VehiclePropConfigBuilder.newBuilder(propertyId);
- mHalConfig.put(builder, new DefaultPropertyHandler(builder.build(), null));
+ setConfigBuilder(builder, new DefaultPropertyHandler(builder.build(), null));
return builder;
}
protected synchronized VehiclePropConfigBuilder addProperty(int propertyId,
VehiclePropValue value) {
VehiclePropConfigBuilder builder = VehiclePropConfigBuilder.newBuilder(propertyId);
- mHalConfig.put(builder, new DefaultPropertyHandler(builder.build(), value));
+ setConfigBuilder(builder, new DefaultPropertyHandler(builder.build(), value));
return builder;
}
@@ -156,10 +195,23 @@
.setChangeMode(VehiclePropertyChangeMode.STATIC)
.setAccess(VehiclePropertyAccess.READ);
- mHalConfig.put(builder, new StaticPropertyHandler(value));
+ setConfigBuilder(builder, new StaticPropertyHandler(value));
return builder;
}
+ private void setConfigBuilder(VehiclePropConfigBuilder builder,
+ VehicleHalPropertyHandler propertyHandler) {
+ int propId = builder.build().prop;
+
+ // Override previous property config if exists.
+ VehiclePropConfigBuilder prevBuilder = mPropToConfigBuilder.get(propId);
+ if (prevBuilder != null) {
+ mHalConfig.remove(prevBuilder);
+ }
+ mPropToConfigBuilder.put(propId, builder);
+ mHalConfig.put(builder, propertyHandler);
+ }
+
protected synchronized android.car.Car getCar() {
return mCar;
}
@@ -179,10 +231,6 @@
mWaitForMain.acquire();
}
- protected boolean waitForFakeDisplayState(boolean expectedState) throws Exception {
- return mFakeSystemInterface.waitForDisplayState(expectedState, SHORT_WAIT_TIMEOUT_MS);
- }
-
public static <T> void assertArrayEquals(T[] expected, T[] actual) {
if (!Arrays.equals(expected, actual)) {
fail("expected:<" + Arrays.toString(expected) +
@@ -239,58 +287,77 @@
}
}
- private static class FakeSystemInterface extends SystemInterface {
-
- private boolean mDisplayOn = true;
- private final Semaphore mDisplayStateWait = new Semaphore(0);
+ static final class MockDisplayInterface implements DisplayInterface {
@Override
- public synchronized void setDisplayState(boolean on) {
- mDisplayOn = on;
- mDisplayStateWait.release();
- }
-
- boolean waitForDisplayState(boolean expectedState, long timeoutMs)
- throws Exception {
- if (expectedState == mDisplayOn) {
- return true;
- }
- mDisplayStateWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS);
- return expectedState == mDisplayOn;
- }
+ public void setDisplayState(boolean on) {}
@Override
- public void releaseAllWakeLocks() {
- }
+ public void startDisplayStateMonitoring(CarPowerManagementService service) {}
@Override
- public void shutdown() { }
-
- @Override
- public void enterDeepSleep(int wakeupTimeSec) { }
-
- @Override
- public boolean isSystemSupportingDeepSleep() {
- return false;
- }
-
- @Override
- public void switchToPartialWakeLock() {
- }
-
- @Override
- public void switchToFullWakeLock() {
- }
-
- @Override
- public void startDisplayStateMonitoring(CarPowerManagementService service) {
- }
-
- @Override
- public void stopDisplayStateMonitoring() {
- }
-
- @Override
- public boolean isWakeupCausedByTimer() { return false; }
+ public void stopDisplayStateMonitoring() {}
}
+
+ static final class MockIOInterface implements IOInterface {
+ private TemporaryDirectory mFilesDir = null;
+
+ @Override
+ public File getFilesDir() {
+ if (mFilesDir == null) {
+ try {
+ mFilesDir = new TemporaryDirectory(TAG);
+ } catch (IOException e) {
+ Log.e(TAG, "failed to create temporary directory", e);
+ fail("failed to create temporary directory. exception was: " + e);
+ }
+ }
+ return mFilesDir.getDirectory();
+ }
+
+ public void tearDown() {
+ if (mFilesDir != null) {
+ try {
+ mFilesDir.close();
+ } catch (Exception e) {
+ Log.w(TAG, "could not remove temporary directory", e);
+ }
+ }
+ }
+ }
+
+ static final class MockStorageMonitoringInterface implements StorageMonitoringInterface {}
+
+ static final class MockSystemStateInterface implements SystemStateInterface {
+ @Override
+ public void shutdown() {}
+
+ @Override
+ public void enterDeepSleep(int wakeupTimeSec) {}
+
+ @Override
+ public void scheduleActionForBootCompleted(Runnable action, Duration delay) {}
+ }
+
+ static final class MockTimeInterface implements TimeInterface {
+
+ @Override
+ public void scheduleAction(Runnable r, long delayMs) {}
+
+ @Override
+ public void cancelAllActions() {}
+ }
+
+ static final class MockWakeLockInterface implements WakeLockInterface {
+
+ @Override
+ public void releaseAllWakeLocks() {}
+
+ @Override
+ public void switchToPartialWakeLock() {}
+
+ @Override
+ public void switchToFullWakeLock() {}
+ }
+
}
diff --git a/tests/carservice_test/src/com/android/car/SimpleVmsPublisherClientService.java b/tests/carservice_test/src/com/android/car/SimpleVmsPublisherClientService.java
new file mode 100644
index 0000000..53491b1
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/SimpleVmsPublisherClientService.java
@@ -0,0 +1,38 @@
+/*
+ * 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.car.vms.VmsPublisherClientService;
+import android.car.vms.VmsSubscriptionState;
+
+/**
+ * This service is launched during the tests in VmsPublisherClientServiceTest.
+ */
+public class SimpleVmsPublisherClientService extends VmsPublisherClientService {
+ @Override
+ public void onVmsSubscriptionChange(VmsSubscriptionState subscriptionState) {
+
+ }
+
+ @Override
+ 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/SystemActivityMonitoringServiceTest.java b/tests/carservice_test/src/com/android/car/SystemActivityMonitoringServiceTest.java
new file mode 100644
index 0000000..9907130
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/SystemActivityMonitoringServiceTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.android.car;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import com.android.car.SystemActivityMonitoringService.TopTaskInfoContainer;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@MediumTest
+public class SystemActivityMonitoringServiceTest extends AndroidTestCase {
+ private static final long ACTIVITY_TIME_OUT = 5000;
+
+ private SystemActivityMonitoringService mService;
+ private Semaphore mSemaphore = new Semaphore(0);
+
+ private final TopTaskInfoContainer[] mTopTaskInfo = new TopTaskInfoContainer[1];
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mService = new SystemActivityMonitoringService(getContext());
+ mService.registerActivityLaunchListener(topTask -> {
+ if (!getTestContext().getPackageName().equals(topTask.topActivity.getPackageName())) {
+ return; // Ignore activities outside of this test case.
+ }
+ synchronized (mTopTaskInfo) {
+ mTopTaskInfo[0] = topTask;
+ }
+ mSemaphore.release();
+ });
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ mService.registerActivityLaunchListener(null);
+ mService = null;
+ }
+
+ public void testActivityLaunch() throws Exception {
+ ComponentName activityA = toComponentName(getTestContext(), ActivityA.class);
+ startActivity(getContext(), activityA);
+ assertTopTaskActivity(activityA);
+
+ ComponentName activityB = toComponentName(getTestContext(), ActivityB.class);
+ startActivity(getContext(), activityB);
+ assertTopTaskActivity(activityB);
+ }
+
+ public void testActivityBlocking() throws Exception {
+ ComponentName blackListedActivity = toComponentName(getTestContext(), ActivityC.class);
+ ComponentName blockingActivity = toComponentName(getTestContext(), BlockingActivity.class);
+ Intent blockingIntent = new Intent();
+ blockingIntent.setComponent(blockingActivity);
+
+ // start a black listed activity
+ startActivity(getContext(), blackListedActivity);
+ assertTopTaskActivity(blackListedActivity);
+
+ // Instead of start activity, invoke blockActivity.
+ mService.blockActivity(mTopTaskInfo[0], blockingIntent);
+ assertTopTaskActivity(blockingActivity);
+ }
+
+ /** Activity that closes itself after some timeout to clean up the screen. */
+ public static class TempActivity extends Activity {
+ @Override
+ protected void onResume() {
+ super.onResume();
+ getMainThreadHandler().postDelayed(this::finish, ACTIVITY_TIME_OUT);
+ }
+ }
+
+ public static class ActivityA extends TempActivity {}
+ public static class ActivityB extends TempActivity {}
+ public static class ActivityC extends TempActivity {}
+ public static class BlockingActivity extends TempActivity {}
+
+ private void assertTopTaskActivity(ComponentName activity) throws Exception{
+ assertTrue(mSemaphore.tryAcquire(2, TimeUnit.SECONDS));
+ synchronized (mTopTaskInfo) {
+ assertEquals(activity, mTopTaskInfo[0].topActivity);
+ }
+ }
+
+ private static ComponentName toComponentName(Context ctx, Class<?> cls) {
+ return ComponentName.createRelative(ctx, cls.getName());
+ }
+
+ private static void startActivity(Context ctx, ComponentName name) {
+ Intent intent = new Intent();
+ intent.setComponent(name);
+ ctx.startActivity(intent);
+ }
+}
\ No newline at end of file
diff --git a/tests/carservice_test/src/com/android/car/test/TestAppBlockingPolicyService.java b/tests/carservice_test/src/com/android/car/TestAppBlockingPolicyService.java
similarity index 98%
rename from tests/carservice_test/src/com/android/car/test/TestAppBlockingPolicyService.java
rename to tests/carservice_test/src/com/android/car/TestAppBlockingPolicyService.java
index dc1ad8a..ee875c4 100644
--- a/tests/carservice_test/src/com/android/car/test/TestAppBlockingPolicyService.java
+++ b/tests/carservice_test/src/com/android/car/TestAppBlockingPolicyService.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.car.test;
+package com.android.car;
import android.car.content.pm.AppBlockingPackageInfo;
import android.car.content.pm.CarAppBlockingPolicy;
@@ -56,6 +56,7 @@
} catch (NameNotFoundException e) {
return null;
}
+
AppBlockingPackageInfo selfInfo = new AppBlockingPackageInfo(packageName, 0, 0, 0,
signatures, null);
AppBlockingPackageInfo[] whitelists = new AppBlockingPackageInfo[] { selfInfo };
diff --git a/tests/carservice_test/src/com/android/car/VmsHalServiceSubscriptionEventTest.java b/tests/carservice_test/src/com/android/car/VmsHalServiceSubscriptionEventTest.java
new file mode 100644
index 0000000..286c004
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/VmsHalServiceSubscriptionEventTest.java
@@ -0,0 +1,174 @@
+/*
+ * 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 static org.junit.Assume.assumeTrue;
+
+import android.car.VehicleAreaType;
+import android.car.vms.VmsLayer;
+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_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;
+import com.android.car.vehiclehal.test.MockedVehicleHal;
+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.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@MediumTest
+public class VmsHalServiceSubscriptionEventTest extends MockedCarTestBase {
+ private static final String TAG = "VmsHalServiceTest";
+
+ private HalHandler mHalHandler;
+ private MockedVehicleHal mHal;
+ // Used to block until the HAL property is updated in HalHandler.onPropertySet.
+ private Semaphore mHalHandlerSemaphore;
+
+ @Override
+ protected synchronized void configureMockedHal() {
+ mHalHandler = new HalHandler();
+ addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
+ .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
+ .setAccess(VehiclePropertyAccess.READ_WRITE)
+ .setSupportedAreas(VehicleAreaType.VEHICLE_AREA_TYPE_NONE);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mHal = getMockedVehicleHal();
+ mHalHandlerSemaphore = new Semaphore(0);
+ }
+
+ @Override
+ protected synchronized void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testEmptySubscriptions() throws Exception {
+ List<VmsLayer> layers = new ArrayList<>();
+ subscriptionTestLogic(layers);
+ }
+
+ public void testOneSubscription() throws Exception {
+ List<VmsLayer> layers = Arrays.asList(new VmsLayer(8, 0, 3));
+ subscriptionTestLogic(layers);
+ }
+
+ public void testManySubscriptions() throws Exception {
+ List<VmsLayer> layers = Arrays.asList(
+ 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);
+ }
+
+ /**
+ * First, it subscribes to the given layers. Then it validates that a subscription request
+ * responds with the same layers.
+ */
+ private void subscriptionTestLogic(List<VmsLayer> layers) throws Exception {
+ for (VmsLayer layer : layers) {
+ subscribeViaHal(layer);
+ }
+ // Send subscription request.
+ mHal.injectEvent(createHalSubscriptionRequest());
+ // Wait for response.
+ assertTrue(mHalHandlerSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ // Validate response.
+ ArrayList<Integer> v = mHalHandler.getValues();
+ 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 = VmsSubscriptionsStateIntegerValuesIndex.SUBSCRIPTIONS_START;
+ int end = VmsSubscriptionsStateIntegerValuesIndex.SUBSCRIPTIONS_START + 3 * numberLayers;
+ while (start < end) {
+ int type = v.get(start++);
+ int subtype = v.get(start++);
+ int version = v.get(start++);
+ receivedLayers.add(new VmsLayer(type, subtype, version));
+ }
+ assertEquals(new HashSet<>(layers), new HashSet<>(receivedLayers));
+ }
+
+ /**
+ * Subscribes to a layer, waits for the event to propagate back to the HAL layer and validates
+ * the propagated message.
+ */
+ private void subscribeViaHal(VmsLayer layer) throws Exception {
+ // Send subscribe request.
+ mHal.injectEvent(createHalSubscribeRequest(layer));
+ // Wait for response.
+ assertTrue(mHalHandlerSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ // Validate response.
+ ArrayList<Integer> v = mHalHandler.getValues();
+ 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.getType(), layerId);
+ assertEquals(layer.getVersion(), layerVersion);
+ }
+
+ private VehiclePropValue createHalSubscribeRequest(VmsLayer layer) {
+ return VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
+ .addIntValue(VmsMessageType.SUBSCRIBE)
+ .addIntValue(layer.getType())
+ .addIntValue(layer.getSubtype())
+ .addIntValue(layer.getVersion())
+ .build();
+ }
+
+ private VehiclePropValue createHalSubscriptionRequest() {
+ return VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
+ .addIntValue(VmsMessageType.SUBSCRIPTIONS_REQUEST)
+ .build();
+ }
+
+ private class HalHandler implements VehicleHalPropertyHandler {
+ private ArrayList<Integer> mValues;
+
+ @Override
+ public synchronized void onPropertySet(VehiclePropValue value) {
+ mValues = value.value.int32Values;
+ mHalHandlerSemaphore.release();
+ }
+
+ public ArrayList<Integer> getValues() {
+ return mValues;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/carservice_test/src/com/android/car/VmsOperationRecorderTest.java b/tests/carservice_test/src/com/android/car/VmsOperationRecorderTest.java
new file mode 100644
index 0000000..f6b3be5
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/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;
+
+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/VmsPublisherClientMockService.java b/tests/carservice_test/src/com/android/car/VmsPublisherClientMockService.java
new file mode 100644
index 0000000..32d224b
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/VmsPublisherClientMockService.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 com.android.car;
+
+import android.car.vms.VmsLayer;
+import android.car.vms.VmsLayerDependency;
+import android.car.vms.VmsLayersOffering;
+import android.car.vms.VmsPublisherClientService;
+import android.car.vms.VmsSubscriptionState;
+import android.util.Log;
+
+import java.util.HashSet;
+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.
+ */
+public class VmsPublisherClientMockService extends VmsPublisherClientService {
+ private static final String TAG = "VmsPublisherClientMockService";
+
+ @Override
+ public void onVmsSubscriptionChange(VmsSubscriptionState subscriptionState) {
+ // Case when the publisher finished initialization before the subscription request.
+ initializeMockPublisher(subscriptionState);
+ }
+
+ @Override
+ public void onVmsPublisherServiceReady() {
+ // Case when the subscription request was sent before the publisher was ready.
+ VmsSubscriptionState subscriptionState = getSubscriptions();
+ initializeMockPublisher(subscriptionState);
+ }
+
+ private void initializeMockPublisher(VmsSubscriptionState subscriptionState) {
+ Log.d(TAG, "Initializing Mock publisher");
+ int publisherId = getPublisherId(VmsPublisherSubscriberTest.PAYLOAD);
+ publishIfNeeded(subscriptionState);
+ declareOffering(subscriptionState, publisherId);
+ }
+
+ private void publishIfNeeded(VmsSubscriptionState subscriptionState) {
+ for (VmsLayer layer : subscriptionState.getLayers()) {
+ if (layer.equals(VmsPublisherSubscriberTest.LAYER)) {
+ publish(VmsPublisherSubscriberTest.LAYER,
+ VmsPublisherSubscriberTest.EXPECTED_PUBLISHER_ID,
+ VmsPublisherSubscriberTest.PAYLOAD);
+ }
+ }
+ }
+
+ 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));
+ }
+
+ // 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/VmsPublisherClientServiceTest.java b/tests/carservice_test/src/com/android/car/VmsPublisherClientServiceTest.java
new file mode 100644
index 0000000..0a53092
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/VmsPublisherClientServiceTest.java
@@ -0,0 +1,197 @@
+/*
+ * 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.ArrayRes;
+import android.car.VehicleAreaType;
+import android.car.vms.VmsLayer;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
+import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode;
+import android.hardware.automotive.vehicle.V2_0.VmsBaseMessageIntegerValuesIndex;
+import android.hardware.automotive.vehicle.V2_0.VmsMessageType;
+import android.hardware.automotive.vehicle.V2_0.VmsMessageWithLayerIntegerValuesIndex;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
+
+import com.android.car.vehiclehal.VehiclePropValueBuilder;
+import com.android.car.vehiclehal.test.MockedVehicleHal;
+import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@MediumTest
+public class VmsPublisherClientServiceTest extends MockedCarTestBase {
+ private static final String TAG = "VmsPublisherTest";
+ private static final int MOCK_PUBLISHER_LAYER_ID = 12;
+ private static final int MOCK_PUBLISHER_LAYER_VERSION = 34;
+ 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;
+ // Used to block until the HAL property is updated in HalHandler.onPropertySet.
+ private Semaphore mHalHandlerSemaphore;
+
+ @Override
+ protected synchronized void configureMockedHal() {
+ mHalHandler = new HalHandler();
+ addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
+ .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
+ .setAccess(VehiclePropertyAccess.READ_WRITE)
+ .setSupportedAreas(VehicleAreaType.VEHICLE_AREA_TYPE_NONE);
+ }
+
+ /**
+ * Creates a context with the resource vmsPublisherClients overridden. The overridden value
+ * contains the name of the test service defined also in this test package.
+ */
+ @Override
+ protected Context getCarServiceContext() throws PackageManager.NameNotFoundException {
+ Context context = getContext()
+ .createPackageContext("com.android.car", Context.CONTEXT_IGNORE_SECURITY);
+ Resources resources = new Resources(context.getAssets(),
+ context.getResources().getDisplayMetrics(),
+ context.getResources().getConfiguration()) {
+ @Override
+ public String[] getStringArray(@ArrayRes int id) throws NotFoundException {
+ if (id == R.array.vmsPublisherClients) {
+ return new String[] {
+ javaClassToComponent(
+ getContext(), SimpleVmsPublisherClientService.class)
+ };
+ }
+
+ return super.getStringArray(id);
+ }
+ };
+ ContextWrapper wrapper = new ContextWrapper(context) {
+ @Override
+ public Resources getResources() {
+ return resources;
+ }
+ };
+ return wrapper;
+ }
+
+ private VehiclePropValue getHalSubscriptionRequest() {
+ return VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
+ .addIntValue(VmsMessageType.SUBSCRIBE)
+ .addIntValue(MOCK_PUBLISHER_LAYER_ID)
+ .addIntValue(MOCK_PUBLISHER_LAYER_SUBTYPE)
+ .addIntValue(MOCK_PUBLISHER_LAYER_VERSION)
+ .build();
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ /**
+ * First init the semaphore, setUp will start a series of events that will ultimately
+ * update the HAL layer and release this semaphore.
+ */
+ mHalHandlerSemaphore = new Semaphore(0);
+ super.setUp();
+
+ // Inject a subscribe event which simulates the HAL is subscribed to the Mock Publisher.
+ MockedVehicleHal mHal = getMockedVehicleHal();
+ mHal.injectEvent(getHalSubscriptionRequest());
+ }
+
+ @Override
+ protected synchronized void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ static String javaClassToComponent(Context context, Class<?> clazz) {
+ return context.getPackageName() + "/" + clazz.getName();
+ }
+
+ /**
+ * The method setUp initializes all the Car services, including the VmsPublisherService.
+ * The VmsPublisherService will start and configure its list of clients. This list was
+ * overridden in the method getCarServiceContext.
+ * Therefore, only SimpleVmsPublisherClientService will be started.
+ * The service SimpleVmsPublisherClientService will publish one message, which is validated in
+ * this test.
+ */
+ public void testPublish() throws Exception {
+ //TODO: This test is using minial synchronisation between clients.
+ // If more complexity is added this may result in publisher
+ // publishing before the subscriber subscribed, in which case
+ // the semaphore will not be released.
+ assertTrue(mHalHandlerSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ VehiclePropValue.RawValue rawValue = mHalHandler.getValue().value;
+ 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);
+ }
+ assertEquals(VmsMessageType.DATA, messageType);
+ assertEquals(MOCK_PUBLISHER_LAYER_ID, layerId);
+ assertEquals(MOCK_PUBLISHER_LAYER_VERSION, layerVersion);
+ assertTrue(Arrays.equals(PAYLOAD, payload));
+ }
+
+ private class HalHandler implements VehicleHalPropertyHandler {
+ private VehiclePropValue mValue;
+
+ @Override
+ public synchronized void onPropertySet(VehiclePropValue value) {
+ mValue = 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.MESSAGE_TYPE) ==
+ VmsMessageType.DATA) {
+ mHalHandlerSemaphore.release();
+ }
+ }
+
+ @Override
+ public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) {
+ return mValue != null ? mValue : value;
+ }
+
+ @Override
+ public synchronized void onPropertySubscribe(int property, int zones, float sampleRate) {
+ Log.d(TAG, "onPropertySubscribe property " + property + " sampleRate " + sampleRate);
+ }
+
+ @Override
+ public synchronized void onPropertyUnsubscribe(int property) {
+ Log.d(TAG, "onPropertyUnSubscribe property " + property);
+ }
+
+ public VehiclePropValue getValue() {
+ return mValue;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/carservice_test/src/com/android/car/VmsPublisherPermissionsTest.java b/tests/carservice_test/src/com/android/car/VmsPublisherPermissionsTest.java
new file mode 100644
index 0000000..14a2f18
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/VmsPublisherPermissionsTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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.ArrayRes;
+import android.car.VehicleAreaType;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+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_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.vehiclehal.VehiclePropValueBuilder;
+import com.android.car.vehiclehal.test.MockedVehicleHal;
+import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
+
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@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.
+ private Semaphore mHalHandlerSemaphore;
+
+ @Override
+ protected synchronized void configureMockedHal() {
+ mHalHandler = new HalHandler();
+ addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
+ .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
+ .setAccess(VehiclePropertyAccess.READ_WRITE)
+ .setSupportedAreas(VehicleAreaType.VEHICLE_AREA_TYPE_NONE);
+ }
+
+ /**
+ * Creates a context with the resource vmsPublisherClients overridden. The overridden value
+ * contains the name of the test service defined also in this test package.
+ */
+ @Override
+ protected Context getCarServiceContext() throws PackageManager.NameNotFoundException {
+ Context context = getContext()
+ .createPackageContext("com.android.car", Context.CONTEXT_IGNORE_SECURITY);
+ Resources resources = new Resources(context.getAssets(),
+ context.getResources().getDisplayMetrics(),
+ context.getResources().getConfiguration()) {
+ @Override
+ public String[] getStringArray(@ArrayRes int id) throws NotFoundException {
+ if (id == R.array.vmsPublisherClients) {
+ return new String[]{
+ "com.google.android.car.vms.publisher/"
+ + ".VmsPublisherClientSampleService"};
+ } else if (id == R.array.vmsSafePermissions) {
+ return new String[]{"android.permission.ACCESS_FINE_LOCATION"};
+ }
+ return super.getStringArray(id);
+ }
+ };
+ ContextWrapper wrapper = new ContextWrapper(context) {
+ @Override
+ public Resources getResources() {
+ return resources;
+ }
+ };
+ return wrapper;
+ }
+
+ private VehiclePropValue getHalSubscriptionRequest() {
+ return VehiclePropValueBuilder.newBuilder(VehicleProperty.VEHICLE_MAP_SERVICE)
+ .addIntValue(VmsMessageType.SUBSCRIBE)
+ .addIntValue(MOCK_PUBLISHER_LAYER_ID)
+ .addIntValue(MOCK_PUBLISHER_LAYER_VERSION)
+ .addIntValue(MOCK_PUBLISHER_LAYER_FUSION_INT_VALUE)
+ .build();
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ /**
+ * First init the semaphore, setUp will start a series of events that will ultimately
+ * update the HAL layer and release this semaphore.
+ */
+ mHalHandlerSemaphore = new Semaphore(0);
+ super.setUp();
+
+ // Inject a subscribe event which simulates the HAL is subscribed to the Sample Publisher.
+ MockedVehicleHal mHal = getMockedVehicleHal();
+ mHal.injectEvent(getHalSubscriptionRequest());
+ }
+
+ @Override
+ protected synchronized void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * The method setUp initializes all the Car services, including the VmsPublisherService.
+ * The VmsPublisherService will start and configure its list of clients. This list was
+ * overridden in the method getCarServiceContext.
+ * Therefore, only VmsPublisherClientSampleService will be started.
+ * The service VmsPublisherClientSampleService will publish one message, which is validated in
+ * this test.
+ */
+ public void testPermissions() throws Exception {
+ assertTrue(mHalHandlerSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ // At this point the client initialization finished. Let's validate the permissions.
+ // The VMS service is only allowed to grant ACCESS_FINE_LOCATION but not CAMERA.
+ assertTrue(
+ getContext().getPackageManager().checkPermission(
+ "android.permission.ACCESS_FINE_LOCATION",
+ "com.google.android.car.vms.publisher")
+ == PackageManager.PERMISSION_GRANTED);
+ assertFalse(getContext().getPackageManager().checkPermission(
+ "android.permission.CAMERA", "com.google.android.car.vms.publisher")
+ == PackageManager.PERMISSION_GRANTED);
+ }
+
+ private class HalHandler implements VehicleHalPropertyHandler {
+ @Override
+ 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.MESSAGE_TYPE) ==
+ VmsMessageType.DATA) {
+ mHalHandlerSemaphore.release();
+ }
+ }
+ }
+}
diff --git a/tests/carservice_test/src/com/android/car/VmsPublisherSubscriberTest.java b/tests/carservice_test/src/com/android/car/VmsPublisherSubscriberTest.java
new file mode 100644
index 0000000..89dcba9
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/VmsPublisherSubscriberTest.java
@@ -0,0 +1,235 @@
+/*
+ * 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 static com.android.car.VmsPublisherClientServiceTest.javaClassToComponent;
+
+import android.annotation.ArrayRes;
+import android.car.Car;
+import android.car.VehicleAreaType;
+import android.car.vms.VmsAssociatedLayer;
+import android.car.vms.VmsLayer;
+import android.car.vms.VmsSubscriberManager;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
+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_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.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@MediumTest
+public class VmsPublisherSubscriberTest extends MockedCarTestBase {
+ private static final int LAYER_ID = 88;
+ private static final int LAYER_VERSION = 19;
+ private static final int LAYER_SUBTYPE = 55;
+ private static final String TAG = "VmsPubSubTest";
+
+ // 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<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 TestClientCallback.onVmsMessageReceived.
+ private Semaphore mSubscriberSemaphore;
+ private Semaphore mAvailabilitySemaphore;
+
+ @Override
+ protected synchronized void configureMockedHal() {
+ mHalHandler = new HalHandler();
+ addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
+ .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
+ .setAccess(VehiclePropertyAccess.READ_WRITE)
+ .setSupportedAreas(VehicleAreaType.VEHICLE_AREA_TYPE_NONE);
+ }
+
+ /**
+ * Creates a context with the resource vmsPublisherClients overridden. The overridden value
+ * contains the name of the test service defined also in this test package.
+ */
+ @Override
+ protected Context getCarServiceContext() throws PackageManager.NameNotFoundException {
+ Context context = getContext()
+ .createPackageContext("com.android.car", Context.CONTEXT_IGNORE_SECURITY);
+ Resources resources = new Resources(context.getAssets(),
+ context.getResources().getDisplayMetrics(),
+ context.getResources().getConfiguration()) {
+ @Override
+ public String[] getStringArray(@ArrayRes int id) throws NotFoundException {
+ if (id == com.android.car.R.array.vmsPublisherClients) {
+ return new String[] {
+ javaClassToComponent(getContext(), VmsPublisherClientMockService.class)
+ };
+ }
+
+ return super.getStringArray(id);
+ }
+ };
+ ContextWrapper wrapper = new ContextWrapper(context) {
+ @Override
+ public Resources getResources() {
+ return resources;
+ }
+ };
+ return wrapper;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mSubscriberSemaphore = new Semaphore(0);
+ mAvailabilitySemaphore = new Semaphore(0);
+ }
+
+ @Override
+ protected synchronized void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /**
+ * The method setUp initializes all the Car services, including the VmsPublisherService.
+ * The VmsPublisherService will start and configure its list of clients. This list was
+ * overridden in the method getCarServiceContext. Therefore, only VmsPublisherClientMockService
+ * will be started. This test method subscribes to a layer and triggers
+ * VmsPublisherClientMockService.onVmsSubscriptionChange. In turn, the mock service will publish
+ * a message, which is validated in this test.
+ */
+ public void testPublisherToSubscriber() throws Exception {
+ VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
+ vmsSubscriberManager.subscribe(LAYER);
+
+ assertTrue(mSubscriberSemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ assertEquals(LAYER, clientCallback.getLayer());
+ assertTrue(Arrays.equals(PAYLOAD, clientCallback.getPayload()));
+ }
+
+ /**
+ * The Mock service will get a publisher ID by sending its information when it will get
+ * ServiceReady as well as on SubscriptionChange. Since clients are not notified when
+ * publishers are assigned IDs, this test waits until the availability is changed which indicates
+ * that the Mock service has gotten its ServiceReady and publisherId.
+ */
+ public void testPublisherInfo() throws Exception {
+ 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.
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
+ vmsSubscriberManager.subscribe(LAYER);
+
+ assertTrue(mAvailabilitySemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+
+ byte[] info = vmsSubscriberManager.getPublisherInfo(EXPECTED_PUBLISHER_ID);
+ assertTrue(Arrays.equals(PAYLOAD, info));
+ }
+
+ /**
+ * 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 testAvailabilityWithSubscription() throws Exception {
+ VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
+ vmsSubscriberManager.subscribe(SUBSCRIBED_LAYER);
+
+ assertTrue(mAvailabilitySemaphore.tryAcquire(2L, TimeUnit.SECONDS));
+ assertEquals(AVAILABLE_ASSOCIATED_LAYERS_WITH_SUBSCRIBED_LAYER, clientCallback.getAvailableLayers());
+ }
+
+ /**
+ * 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 {
+ 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.getAvailableLayers());
+ }
+
+ private class HalHandler implements MockedVehicleHal.VehicleHalPropertyHandler {
+ }
+
+ private class TestClientCallback implements VmsSubscriberManager.VmsSubscriberClientCallback {
+ private VmsLayer mLayer;
+ private byte[] mPayload;
+ private List<VmsLayer> mAvailableLayers;
+
+ @Override
+ public void onVmsMessageReceived(VmsLayer layer, byte[] payload) {
+ assertEquals(LAYER, layer);
+ assertTrue(Arrays.equals(PAYLOAD, payload));
+ mLayer = layer;
+ mPayload = payload;
+ mSubscriberSemaphore.release();
+ }
+
+ @Override
+ public void onLayersAvailabilityChanged(List<VmsLayer> availableLayers) {
+ mAvailableLayers = availableLayers;
+ mAvailabilitySemaphore.release();
+ }
+
+ public VmsLayer getLayer() {
+ return mLayer;
+ }
+
+ public byte[] getPayload() {
+ return mPayload;
+ }
+
+ public List<VmsLayer> getAvailableLayers() {
+ return mAvailableLayers;
+ }
+ }
+}
diff --git a/tests/carservice_test/src/com/android/car/VmsSubscriberManagerTest.java b/tests/carservice_test/src/com/android/car/VmsSubscriberManagerTest.java
new file mode 100644
index 0000000..954afee
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/VmsSubscriberManagerTest.java
@@ -0,0 +1,598 @@
+/*
+ * 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 static org.junit.Assume.assumeTrue;
+
+import android.car.Car;
+import android.car.VehicleAreaType;
+import android.car.vms.VmsAssociatedLayer;
+import android.car.vms.VmsLayer;
+import android.car.vms.VmsSubscriberManager;
+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_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;
+
+@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,
+ 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,
+ 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;
+
+
+ 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 TestClientCallback.onVmsMessageReceived.
+ private Semaphore mSubscriberSemaphore;
+
+ @Override
+ protected synchronized void configureMockedHal() {
+ mHalHandler = new HalHandler();
+ addProperty(VehicleProperty.VEHICLE_MAP_SERVICE, mHalHandler)
+ .setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
+ .setAccess(VehiclePropertyAccess.READ_WRITE)
+ .setSupportedAreas(VehicleAreaType.VEHICLE_AREA_TYPE_NONE);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mSubscriberSemaphore = new Semaphore(0);
+ mHalHandlerSemaphore = new Semaphore(0);
+ }
+
+ @Override
+ protected synchronized void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ // Test injecting a value in the HAL and verifying it propagates to a subscriber.
+ public void testSubscribe() throws Exception {
+ 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.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 propagates to a subscriber.
+ public void testSubscribeToPublisher() throws Exception {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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));
+ }
+
+
+ // Test injecting a value in the HAL and verifying it propagates to a subscriber.
+ public void testSubscribeAll() throws Exception {
+ VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
+ vmsSubscriberManager.startMonitoring();
+
+ // 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 propagates to a subscriber.
+ public void testSimpleAvailableLayers() throws Exception {
+ VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
+
+ // 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));
+ 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 {
+ 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));
+ 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 {
+ 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 {
+ 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 {
+ VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE);
+ TestClientCallback clientCallback = new TestClientCallback();
+ vmsSubscriberManager.registerClientCallback(clientCallback);
+
+ // 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] | {}
+ (4, 5, 444), [17] | {(2, 3)}
+ (6, 7, 444), [17] | {(2, 3), (4, 5)}
+ (6, 7, 444), [17] | {(100, 200)}
+
+ Expected availability:
+ {(2, 3, 444 [17]), (4, 5, 444 [17]), (6, 7, 444 [17])}
+ */
+
+ v.value.int32Values.addAll(
+ Arrays.asList(
+ VmsMessageType.OFFERING, // MessageType
+ PUBLISHER_ID,
+ 4, // Number of offered layers
+
+ SUBSCRIPTION_LAYER_ID,
+ MOCK_PUBLISHER_LAYER_SUBTYPE,
+ SUBSCRIPTION_LAYER_VERSION,
+ 0, // number of dependencies for layer
+
+ 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,
+ 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,
+ 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<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));
+ assertTrue(expectedAvailableLayers.containsAll(clientCallback.getAvailableLayers()));
+ assertEquals(expectedAvailableLayers.size(), clientCallback.getAvailableLayers().size());
+ }
+
+ private class HalHandler implements VehicleHalPropertyHandler {
+ private VehiclePropValue mValue;
+
+ @Override
+ public synchronized void onPropertySet(VehiclePropValue value) {
+ mValue = value;
+ mHalHandlerSemaphore.release();
+ }
+
+ @Override
+ public synchronized VehiclePropValue onPropertyGet(VehiclePropValue value) {
+ return mValue != null ? mValue : value;
+ }
+
+ @Override
+ public synchronized void onPropertySubscribe(int property, int zones, float sampleRate) {
+ Log.d(TAG, "onPropertySubscribe property " + property + " sampleRate " + sampleRate);
+ }
+
+ @Override
+ public synchronized void onPropertyUnsubscribe(int property) {
+ Log.d(TAG, "onPropertyUnSubscribe property " + property);
+ }
+
+ public VehiclePropValue getValue() {
+ return mValue;
+ }
+ }
+
+ private class TestClientCallback implements VmsSubscriberClientCallback {
+ private VmsLayer mLayer;
+ private byte[] mPayload;
+ private List<VmsLayer> mAvailableLayers = new ArrayList<>();
+
+ @Override
+ public void onVmsMessageReceived(VmsLayer layer, byte[] payload) {
+ Log.d(TAG, "onVmsMessageReceived: layer: " + layer + " Payload: " + payload);
+ mLayer = layer;
+ mPayload = payload;
+ mSubscriberSemaphore.release();
+ }
+
+ @Override
+ public void onLayersAvailabilityChanged(List<VmsLayer> availableLayers) {
+ Log.d(TAG, "onLayersAvailabilityChanged: Layers: " + availableLayers);
+ mAvailableLayers.addAll(availableLayers);
+ mSubscriberSemaphore.release();
+ }
+
+ public VmsLayer getLayer() {
+ return mLayer;
+ }
+
+ public byte[] getPayload() {
+ return mPayload;
+ }
+
+ public List<VmsLayer> getAvailableLayers() {
+ return mAvailableLayers;
+ }
+ }
+}
diff --git a/tests/carservice_test/src/com/android/car/test/SystemActivityMonitoringServiceTest.java b/tests/carservice_test/src/com/android/car/test/SystemActivityMonitoringServiceTest.java
deleted file mode 100644
index 3345605..0000000
--- a/tests/carservice_test/src/com/android/car/test/SystemActivityMonitoringServiceTest.java
+++ /dev/null
@@ -1,227 +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.android.car.test;
-
-import android.app.Activity;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.hardware.automotive.vehicle.V2_0.VehicleDrivingStatus;
-import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
-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;
-import com.android.car.vehiclehal.VehiclePropValueBuilder;
-import com.android.car.vehiclehal.test.MockedVehicleHal.VehicleHalPropertyHandler;
-
-import java.util.ArrayList;
-import java.util.List;
-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;
- private static final Semaphore sAvailable = new Semaphore(0);
-
- private final DrivingStatusHandler mDrivingStatusHandler = new DrivingStatusHandler();
-
- @Override
- protected synchronized void configureMockedHal() {
- addProperty(VehicleProperty.DRIVING_STATUS, mDrivingStatusHandler)
- .setAccess(VehiclePropertyAccess.READ);
- }
-
- private void init(boolean drivingStatusRestricted) {
- // Set no restriction to driving status, to avoid CarPackageManagerService to launch a
- // 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())
- .addIntValue(0)
- .build();
- getMockedVehicleHal().injectEvent(injectValue);
- }
-
- public void testActivityLaunch() {
- init(false);
- List<TopTaskInfoContainer> taskList = new ArrayList<>();
- SystemActivityMonitoringService systemActivityMonitoringService =
- new SystemActivityMonitoringService(getContext());
- systemActivityMonitoringService.registerActivityLaunchListener(
- new SystemActivityMonitoringService.ActivityLaunchListener() {
- @Override
- public void onActivityLaunch(
- SystemActivityMonitoringService.TopTaskInfoContainer topTask) {
- taskList.add(topTask);
- }
- });
- getContext().startActivity(new Intent(getContext(), ActivityA.class));
- verifyTopActivityPolling(taskList, 0, new ComponentName(getContext().getPackageName(),
- ActivityA.class.getName()));
- sAvailable.release();
-
- verifyTopActivityPolling(taskList, 1, new ComponentName(getContext().getPackageName(),
- ActivityB.class.getName()));
- sAvailable.release();
-
- verifyTopActivityPolling(taskList, 2, new ComponentName(getContext().getPackageName(),
- ActivityC.class.getName()));
- }
-
- public void testActivityBlocking() {
- init(false);
- Semaphore blocked = new Semaphore(0);
- List<TopTaskInfoContainer> taskList = new ArrayList<>();
- SystemActivityMonitoringService systemActivityMonitoringService =
- new SystemActivityMonitoringService(getContext());
-
- ComponentName blackListedActivity = new ComponentName(getContext().getPackageName(),
- ActivityC.class.getName());
- ComponentName blockingActivity = new ComponentName(getContext().getPackageName(),
- BlockingActivity.class.getName());
- Intent newActivityIntent = new Intent();
- newActivityIntent.setComponent(blockingActivity);
-
- systemActivityMonitoringService.registerActivityLaunchListener(
- new SystemActivityMonitoringService.ActivityLaunchListener() {
- @Override
- public void onActivityLaunch(
- SystemActivityMonitoringService.TopTaskInfoContainer topTask) {
- taskList.add(topTask);
- if (topTask.topActivity.equals(blackListedActivity)) {
- systemActivityMonitoringService.blockActivity(topTask,
- newActivityIntent);
- blocked.release();
- }
- }
- });
- // start a black listed activity
- getContext().startActivity(new Intent(getContext(), ActivityC.class));
- // wait for the listener to call blockActivity()
- try {
- blocked.tryAcquire(2, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- fail(e.getMessage());
- }
- // We should first receive the blackListedActivity launch,
- // and later the blockActivity launch
- verifyTopActivityPolling(taskList, 0, blackListedActivity);
- verifyTopActivityPolling(taskList, 1, blockingActivity);
- }
-
- private void verifyTopActivityPolling(
- List<TopTaskInfoContainer> topTaskList, int i, ComponentName activity) {
- boolean activityVerified = false;
- int timeElapsedMs = 0;
- try {
- while (!activityVerified && timeElapsedMs <= TIMEOUT_MS) {
- Thread.sleep(POLL_INTERVAL_MS);
- timeElapsedMs += POLL_INTERVAL_MS;
- if (topTaskList.size() <= i) continue;
- TopTaskInfoContainer topTask = topTaskList.get(i);
- if (topTask != null && topTask.topActivity.equals(activity)) {
- activityVerified = true;
- break;
- }
- }
- assertEquals(true, activityVerified);
- } catch (Exception e) {
- fail(e.toString());
- }
- }
-
- public static class ActivityA extends Activity {
- @Override
- protected void onPostResume() {
- super.onPostResume();
- // Wait until the activity launch event is consumed by the listener.
- try {
- if (!sAvailable.tryAcquire(2, TimeUnit.SECONDS)) {
- fail("Time out");
- }
- } catch (Exception e) {
- fail(e.toString());
- }
- Intent intent = new Intent(this, ActivityB.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
- }
- }
-
- public static class ActivityB extends Activity {
- @Override
- protected void onPostResume() {
- super.onPostResume();
- // Wait until the activity launch event is consumed by the listener.
- try {
- if (!sAvailable.tryAcquire(2, TimeUnit.SECONDS)) {
- fail("Time out");
- }
- } catch (Exception e) {
- fail(e.toString());
- }
- Intent intent = new Intent(this, ActivityC.class);
- startActivity(intent);
- }
- }
-
- public static class ActivityC extends Activity {
- }
-
- public static class BlockingActivity extends Activity {
- }
-
- private class DrivingStatusHandler implements VehicleHalPropertyHandler {
- int mDrivingStatus = VehicleDrivingStatus.UNRESTRICTED;
-
- public void setDrivingStatusRestricted(boolean restricted) {
- mDrivingStatus = restricted ? VehicleDrivingStatus.NO_VIDEO
- : VehicleDrivingStatus.UNRESTRICTED;
- }
-
- @Override
- public void onPropertySet(VehiclePropValue value) {
- }
-
- @Override
- public VehiclePropValue onPropertyGet(VehiclePropValue value) {
- return VehiclePropValueBuilder.newBuilder(VehicleProperty.DRIVING_STATUS)
- .setTimestamp(SystemClock.elapsedRealtimeNanos())
- .addIntValue(mDrivingStatus)
- .build();
- }
-
- @Override
- public void onPropertySubscribe(int property, int zones, float sampleRate) {
- }
-
- @Override
- public void onPropertyUnsubscribe(int property) {
- }
- }
-}
\ No newline at end of file
diff --git a/tests/carservice_unit_test/Android.mk b/tests/carservice_unit_test/Android.mk
index 1ec2008..1cf134c 100644
--- a/tests/carservice_unit_test/Android.mk
+++ b/tests/carservice_unit_test/Android.mk
@@ -34,6 +34,10 @@
LOCAL_INSTRUMENTATION_FOR := CarService
LOCAL_JAVA_LIBRARIES := android.car android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test android-support-test mockito-target-minus-junit4
+LOCAL_STATIC_JAVA_LIBRARIES := junit \
+ legacy-android-test \
+ android-support-test \
+ mockito-target-minus-junit4 \
+ com.android.car.test.utils
include $(BUILD_PACKAGE)
diff --git a/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java b/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
index 78f13e3..db24049 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
@@ -25,6 +25,15 @@
import com.android.car.hal.PowerHalService;
import com.android.car.hal.PowerHalService.PowerState;
+import com.android.car.systeminterface.DisplayInterface;
+import com.android.car.systeminterface.IOInterface;
+import com.android.car.systeminterface.SystemInterface;
+import com.android.car.systeminterface.SystemStateInterface;
+import com.android.car.systeminterface.WakeLockInterface;
+import com.android.car.test.utils.TemporaryDirectory;
+import java.io.File;
+import java.io.IOException;
+import java.time.Duration;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
@@ -34,8 +43,13 @@
private static final long WAIT_TIMEOUT_MS = 2000;
private static final long WAIT_TIMEOUT_LONG_MS = 5000;
+ private final MockDisplayInterface mDisplayInterface = new MockDisplayInterface();
+ private final MockSystemStateInterface mSystemStateInterface = new MockSystemStateInterface();
+ private final MockWakeLockInterface mWakeLockInterface = new MockWakeLockInterface();
+ private final MockIOInterface mIOInterface = new MockIOInterface();
+
private MockedPowerHalService mPowerHal;
- private SystemInterfaceImpl mSystemInterface;
+ private SystemInterface mSystemInterface;
private CarPowerManagementService mService;
private final PowerEventListener mPowerEventListener = new PowerEventListener();
private PowerEventProcessingHandlerImpl mPowerEventProcessingHandler;
@@ -45,7 +59,11 @@
super.setUp();
mPowerHal = new MockedPowerHalService(true /*isPowerStateSupported*/,
true /*isDeepSleepAllowed*/, true /*isTimedWakeupAllowed*/);
- mSystemInterface = new SystemInterfaceImpl();
+ mSystemInterface = SystemInterface.Builder.defaultSystemInterface(getContext())
+ .withDisplayInterface(mDisplayInterface)
+ .withSystemStateInterface(mSystemStateInterface)
+ .withWakeLockInterface(mWakeLockInterface)
+ .withIOInterface(mIOInterface).build();
}
@Override
@@ -54,6 +72,7 @@
if (mService != null) {
mService.release();
}
+ mIOInterface.tearDown();
}
public void testBootComplete() throws Exception {
@@ -75,15 +94,15 @@
assertStateReceived(MockedPowerHalService.SET_BOOT_COMPLETE, 0);
mPowerEventProcessingHandler.waitForPowerOn(WAIT_TIMEOUT_MS);
// it will call display on for initial state
- assertTrue(mSystemInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
+ assertTrue(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
mPowerHal.setCurrentPowerState(new PowerState(PowerHalService.STATE_ON_DISP_OFF, 0));
- assertFalse(mSystemInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
+ assertFalse(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
}
public void testDisplayOn() throws Exception {
// start with display off
mSystemInterface.setDisplayState(false);
- mSystemInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS);
+ mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS);
mService = new CarPowerManagementService(mPowerHal, mSystemInterface);
mService.init();
mService.registerPowerEventListener(mPowerEventListener);
@@ -93,7 +112,7 @@
mPowerEventProcessingHandler.waitForPowerOn(WAIT_TIMEOUT_MS);
// display should be turned on as it started with off state.
- assertTrue(mSystemInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
+ assertTrue(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
}
public void testShutdown() throws Exception {
@@ -105,14 +124,14 @@
mService.registerPowerEventProcessingHandler(mPowerEventProcessingHandler);
assertStateReceived(MockedPowerHalService.SET_BOOT_COMPLETE, 0);
mPowerEventProcessingHandler.waitForPowerOn(WAIT_TIMEOUT_MS);
- assertTrue(mSystemInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
+ assertTrue(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
mPowerHal.setCurrentPowerState(new PowerState(PowerHalService.STATE_SHUTDOWN_PREPARE,
PowerHalService.FLAG_SHUTDOWN_IMMEDIATELY));
assertStateReceived(PowerHalService.SET_SHUTDOWN_START, wakeupTime);
- assertFalse(mSystemInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
+ assertFalse(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
mPowerEventListener.waitForShutdown(WAIT_TIMEOUT_MS);
- mSystemInterface.waitForShutdown(WAIT_TIMEOUT_MS);
+ mSystemStateInterface.waitForShutdown(WAIT_TIMEOUT_MS);
}
public void testShutdownWithProcessing() throws Exception {
@@ -126,15 +145,15 @@
mService.registerPowerEventProcessingHandler(mPowerEventProcessingHandler);
assertStateReceived(MockedPowerHalService.SET_BOOT_COMPLETE, 0);
mPowerEventProcessingHandler.waitForPowerOn(WAIT_TIMEOUT_MS);
- assertTrue(mSystemInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
+ assertTrue(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
mPowerHal.setCurrentPowerState(new PowerState(PowerHalService.STATE_SHUTDOWN_PREPARE, 0));
mPowerEventProcessingHandler.waitForPrepareShutdown(WAIT_TIMEOUT_MS);
assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_SHUTDOWN_START,
WAIT_TIMEOUT_LONG_MS, wakeupTime);
- assertFalse(mSystemInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
+ assertFalse(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
mPowerEventListener.waitForShutdown(WAIT_TIMEOUT_MS);
- mSystemInterface.waitForShutdown(WAIT_TIMEOUT_MS);
+ mSystemStateInterface.waitForShutdown(WAIT_TIMEOUT_MS);
}
public void testSleepEntryAndWakeup() throws Exception {
@@ -146,14 +165,14 @@
mService.registerPowerEventProcessingHandler(mPowerEventProcessingHandler);
assertStateReceived(MockedPowerHalService.SET_BOOT_COMPLETE, 0);
mPowerEventProcessingHandler.waitForPowerOn(WAIT_TIMEOUT_MS);
- assertTrue(mSystemInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
+ assertTrue(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
mPowerHal.setCurrentPowerState(new PowerState(PowerHalService.STATE_SHUTDOWN_PREPARE,
PowerHalService.FLAG_SHUTDOWN_PARAM_CAN_SLEEP));
- assertFalse(mSystemInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
+ assertFalse(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
assertStateReceived(PowerHalService.SET_DEEP_SLEEP_ENTRY, 0);
mPowerEventListener.waitForSleepEntry(WAIT_TIMEOUT_MS);
- int wakeupTimeReceived = mSystemInterface.waitForSleepEntryAndWakeup(WAIT_TIMEOUT_MS);
+ int wakeupTimeReceived = mSystemStateInterface.waitForSleepEntryAndWakeup(WAIT_TIMEOUT_MS);
assertEquals(wakeupTime, wakeupTimeReceived);
assertStateReceived(PowerHalService.SET_DEEP_SLEEP_EXIT, 0);
mPowerEventListener.waitForSleepExit(WAIT_TIMEOUT_MS);
@@ -171,19 +190,19 @@
mService.registerPowerEventProcessingHandler(mPowerEventProcessingHandler);
assertStateReceived(MockedPowerHalService.SET_BOOT_COMPLETE, 0);
mPowerEventProcessingHandler.waitForPowerOn(WAIT_TIMEOUT_MS);
- assertTrue(mSystemInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
+ assertTrue(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
mPowerHal.setCurrentPowerState(new PowerState(PowerHalService.STATE_SHUTDOWN_PREPARE,
PowerHalService.FLAG_SHUTDOWN_PARAM_CAN_SLEEP));
mPowerEventProcessingHandler.waitForPrepareShutdown(WAIT_TIMEOUT_MS);
- assertFalse(mSystemInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
+ assertFalse(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_DEEP_SLEEP_ENTRY,
WAIT_TIMEOUT_LONG_MS, 0);
mPowerEventListener.waitForSleepEntry(WAIT_TIMEOUT_MS);
// set power on here without notification. PowerManager should check the state after sleep
// exit
mPowerHal.setCurrentPowerState(new PowerState(PowerHalService.STATE_ON_DISP_OFF, 0), false);
- int wakeupTimeReceived = mSystemInterface.waitForSleepEntryAndWakeup(WAIT_TIMEOUT_MS);
+ int wakeupTimeReceived = mSystemStateInterface.waitForSleepEntryAndWakeup(WAIT_TIMEOUT_MS);
assertEquals(wakeupTime, wakeupTimeReceived);
assertStateReceived(PowerHalService.SET_DEEP_SLEEP_EXIT, 0);
mPowerEventListener.waitForSleepExit(WAIT_TIMEOUT_MS);
@@ -201,35 +220,35 @@
mService.registerPowerEventProcessingHandler(mPowerEventProcessingHandler);
assertStateReceived(MockedPowerHalService.SET_BOOT_COMPLETE, 0);
mPowerEventProcessingHandler.waitForPowerOn(WAIT_TIMEOUT_MS);
- assertTrue(mSystemInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
+ assertTrue(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
mPowerHal.setCurrentPowerState(new PowerState(PowerHalService.STATE_SHUTDOWN_PREPARE,
PowerHalService.FLAG_SHUTDOWN_PARAM_CAN_SLEEP));
mPowerEventProcessingHandler.waitForPrepareShutdown(WAIT_TIMEOUT_MS);
- assertFalse(mSystemInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
+ assertFalse(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS));
assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_DEEP_SLEEP_ENTRY,
WAIT_TIMEOUT_LONG_MS, 0);
mPowerEventListener.waitForSleepEntry(WAIT_TIMEOUT_MS);
- mSystemInterface.setWakeupCausedByTimer(true);
- int wakeupTimeReceived = mSystemInterface.waitForSleepEntryAndWakeup(WAIT_TIMEOUT_MS);
+ mSystemStateInterface.setWakeupCausedByTimer(true);
+ int wakeupTimeReceived = mSystemStateInterface.waitForSleepEntryAndWakeup(WAIT_TIMEOUT_MS);
assertEquals(wakeupTime, wakeupTimeReceived);
assertStateReceived(PowerHalService.SET_DEEP_SLEEP_EXIT, 0);
mPowerEventListener.waitForSleepExit(WAIT_TIMEOUT_MS);
// second processing after wakeup
- assertFalse(mSystemInterface.getDisplayState());
+ assertFalse(mDisplayInterface.getDisplayState());
mPowerEventProcessingHandler.waitForPrepareShutdown(WAIT_TIMEOUT_MS);
assertStateReceivedForShutdownOrSleepWithPostpone(PowerHalService.SET_DEEP_SLEEP_ENTRY,
WAIT_TIMEOUT_LONG_MS, 0);
mPowerEventListener.waitForSleepEntry(WAIT_TIMEOUT_MS);
// PM will shutdown system as it was not woken-up due to timer and it is not power on.
- mSystemInterface.setWakeupCausedByTimer(false);
- wakeupTimeReceived = mSystemInterface.waitForSleepEntryAndWakeup(WAIT_TIMEOUT_MS);
+ mSystemStateInterface.setWakeupCausedByTimer(false);
+ wakeupTimeReceived = mSystemStateInterface.waitForSleepEntryAndWakeup(WAIT_TIMEOUT_MS);
assertEquals(wakeupTime, wakeupTimeReceived);
assertStateReceived(PowerHalService.SET_DEEP_SLEEP_EXIT, 0);
assertStateReceived(PowerHalService.SET_SHUTDOWN_START, wakeupTime);
mPowerEventListener.waitForShutdown(WAIT_TIMEOUT_MS);
- mSystemInterface.waitForShutdown(WAIT_TIMEOUT_MS);
- assertFalse(mSystemInterface.getDisplayState());
+ mSystemStateInterface.waitForShutdown(WAIT_TIMEOUT_MS);
+ assertFalse(mDisplayInterface.getDisplayState());
}
private void assertStateReceived(int expectedState, int expectedParam) throws Exception {
@@ -259,15 +278,9 @@
}
}
- private static class SystemInterfaceImpl extends SystemInterface {
-
+ private static final class MockDisplayInterface implements DisplayInterface {
private boolean mDisplayOn = true;
private final Semaphore mDisplayStateWait = new Semaphore(0);
- private final Semaphore mShutdownWait = new Semaphore(0);
- private final Semaphore mSleepWait = new Semaphore(0);
- private final Semaphore mSleepExitWait = new Semaphore(0);
- private int mWakeupTime;
- private boolean mWakeupCausedByTimer = false;
@Override
public synchronized void setDisplayState(boolean on) {
@@ -285,8 +298,18 @@
}
@Override
- public void releaseAllWakeLocks() {
- }
+ public void startDisplayStateMonitoring(CarPowerManagementService service) {}
+
+ @Override
+ public void stopDisplayStateMonitoring() {}
+ }
+
+ private static final class MockSystemStateInterface implements SystemStateInterface {
+ private final Semaphore mShutdownWait = new Semaphore(0);
+ private final Semaphore mSleepWait = new Semaphore(0);
+ private final Semaphore mSleepExitWait = new Semaphore(0);
+ private int mWakeupTime;
+ private boolean mWakeupCausedByTimer = false;
@Override
public void shutdown() {
@@ -307,11 +330,6 @@
}
}
- @Override
- public boolean isSystemSupportingDeepSleep() {
- return true;
- }
-
public int waitForSleepEntryAndWakeup(long timeoutMs) throws Exception {
waitForSemaphore(mSleepWait, timeoutMs);
mSleepExitWait.release();
@@ -319,23 +337,10 @@
}
@Override
- public void switchToPartialWakeLock() {
- }
+ public void scheduleActionForBootCompleted(Runnable action, Duration delay) {}
@Override
- public void switchToFullWakeLock() {
- }
-
- @Override
- public void startDisplayStateMonitoring(CarPowerManagementService service) {
- }
-
- @Override
- public void stopDisplayStateMonitoring() {
- }
-
- @Override
- public synchronized boolean isWakeupCausedByTimer() {
+ public boolean isWakeupCausedByTimer() {
Log.i(TAG, "isWakeupCausedByTimer:" + mWakeupCausedByTimer);
return mWakeupCausedByTimer;
}
@@ -343,6 +348,50 @@
public synchronized void setWakeupCausedByTimer(boolean set) {
mWakeupCausedByTimer = set;
}
+
+ @Override
+ public boolean isSystemSupportingDeepSleep() {
+ return true;
+ }
+ }
+
+ private static final class MockWakeLockInterface implements WakeLockInterface {
+
+ @Override
+ public void releaseAllWakeLocks() {}
+
+ @Override
+ public void switchToPartialWakeLock() {}
+
+ @Override
+ public void switchToFullWakeLock() {}
+ }
+
+ private static final class MockIOInterface implements IOInterface {
+ private TemporaryDirectory mFilesDir;
+
+ @Override
+ public File getFilesDir() {
+ if (mFilesDir == null) {
+ try {
+ mFilesDir = new TemporaryDirectory(TAG);
+ } catch (IOException e) {
+ Log.e(TAG, "failed to create temporary directory", e);
+ fail("failed to create temporary directory. exception was: " + e);
+ }
+ }
+ return mFilesDir.getDirectory();
+ }
+
+ public void tearDown() {
+ if (mFilesDir != null) {
+ try {
+ mFilesDir.close();
+ } catch (Exception e) {
+ Log.w(TAG, "could not remove temporary directory", e);
+ }
+ }
+ }
}
private class PowerEventListener implements PowerServiceEventListener {
diff --git a/tests/carservice_unit_test/src/com/android/car/SlidingWindowTest.java b/tests/carservice_unit_test/src/com/android/car/SlidingWindowTest.java
new file mode 100644
index 0000000..5d372d9
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/SlidingWindowTest.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.android.car;
+
+import android.test.suitebuilder.annotation.MediumTest;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import junit.framework.TestCase;
+
+@MediumTest
+public class SlidingWindowTest extends TestCase {
+ static final String TAG = SlidingWindowTest.class.getSimpleName();
+
+ private static <T> boolean sameContents(Iterator<T> t1, Iterator<T> t2) {
+ while(t1.hasNext()) {
+ if (!t2.hasNext()) {
+ return false;
+ }
+ T e1 = t1.next();
+ T e2 = t2.next();
+ if (!e1.equals(e2)) {
+ return false;
+ }
+ }
+ return !t2.hasNext();
+ }
+
+ public void testAdd() {
+ final List<Integer> elements = Arrays.asList(3, 4);
+ SlidingWindow<Integer> window = new SlidingWindow<>(5);
+ elements.forEach(window::add);
+ assertTrue(sameContents(elements.iterator(), window.iterator()));
+ }
+
+ public void testAddOverflow() {
+ final List<Integer> elements = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+ final List<Integer> expectedElements = Arrays.asList(6, 7, 8, 9, 10);
+ SlidingWindow<Integer> window = new SlidingWindow<>(5);
+ window.addAll(elements);
+ assertTrue(sameContents(expectedElements.iterator(), window.iterator()));
+ }
+
+ public void testStream() {
+ final List<Integer> elements = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+ final List<Integer> expectedElements = Arrays.asList(6, 7, 8, 9, 10);
+ SlidingWindow<Integer> window = new SlidingWindow<>(5);
+ window.addAll(elements);
+ for (Integer e : expectedElements) {
+ assertTrue(window.stream().anyMatch(e::equals));
+ }
+ }
+
+ public void testCount() {
+ final List<Integer> elements = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+ SlidingWindow<Integer> window = new SlidingWindow<>(5);
+ window.addAll(elements);
+ assertEquals(3, window.count((Integer e) -> (e % 2) == 0));
+ }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/UptimeTrackerTest.java b/tests/carservice_unit_test/src/com/android/car/UptimeTrackerTest.java
new file mode 100644
index 0000000..1b46825
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/UptimeTrackerTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.test.suitebuilder.annotation.MediumTest;
+import com.android.car.systeminterface.TimeInterface;
+import com.android.car.test.utils.TemporaryFile;
+
+import junit.framework.TestCase;
+
+@MediumTest
+public class UptimeTrackerTest extends TestCase {
+ static final String TAG = UptimeTrackerTest.class.getSimpleName();
+
+ static final class MockTimeInterface implements TimeInterface {
+ private long mCurrentTime = 0;
+ private Runnable mRunnable = null;
+
+ MockTimeInterface incrementTime(long by) {
+ mCurrentTime += by;
+ return this;
+ }
+
+ MockTimeInterface tick() {
+ if (mRunnable != null) {
+ mRunnable.run();
+ }
+ return this;
+ }
+
+ @Override
+ public long getUptime(boolean includeDeepSleepTime) {
+ return mCurrentTime;
+ }
+
+ @Override
+ public void scheduleAction(Runnable r, long delayMs) {
+ if (mRunnable != null) {
+ throw new IllegalStateException("task already scheduled");
+ }
+ mRunnable = r;
+ }
+
+ @Override
+ public void cancelAllActions() {
+ mRunnable = null;
+ }
+ }
+
+ private static final long SNAPSHOT_INTERVAL = 0; // actual time doesn't matter for this test
+
+ public void testUptimeTrackerFromCleanSlate() throws Exception {
+ MockTimeInterface timeInterface = new MockTimeInterface();
+ try (TemporaryFile uptimeFile = new TemporaryFile(TAG)) {
+ UptimeTracker uptimeTracker = new UptimeTracker(uptimeFile.getFile(),
+ SNAPSHOT_INTERVAL, timeInterface);
+
+ assertEquals(0, uptimeTracker.getTotalUptime());
+
+ timeInterface.incrementTime(5000).tick();
+ assertEquals(5000, uptimeTracker.getTotalUptime());
+
+ timeInterface.tick();
+ assertEquals(5000, uptimeTracker.getTotalUptime());
+
+ timeInterface.incrementTime(1000).tick();
+ assertEquals(6000, uptimeTracker.getTotalUptime());
+
+ timeInterface.incrementTime(400).tick();
+ assertEquals(6400, uptimeTracker.getTotalUptime());
+ }
+ }
+
+ public void testUptimeTrackerWithHistoricalState() throws Exception {
+ MockTimeInterface timeInterface = new MockTimeInterface();
+ try (TemporaryFile uptimeFile = new TemporaryFile(TAG)) {
+ uptimeFile.write("{\"uptime\" : 5000}");
+ UptimeTracker uptimeTracker = new UptimeTracker(uptimeFile.getFile(),
+ SNAPSHOT_INTERVAL, timeInterface);
+
+ assertEquals(5000, uptimeTracker.getTotalUptime());
+
+ timeInterface.incrementTime(5000).tick();
+ assertEquals(10000, uptimeTracker.getTotalUptime());
+
+ timeInterface.incrementTime(1000).tick();
+ assertEquals(11000, uptimeTracker.getTotalUptime());
+ }
+ }
+
+ public void testUptimeTrackerAcrossHistoricalState() throws Exception {
+ MockTimeInterface timeInterface = new MockTimeInterface();
+ try (TemporaryFile uptimeFile = new TemporaryFile(TAG)) {
+ uptimeFile.write("{\"uptime\" : 5000}");
+ UptimeTracker uptimeTracker = new UptimeTracker(uptimeFile.getFile(),
+ SNAPSHOT_INTERVAL, timeInterface);
+
+ assertEquals(5000, uptimeTracker.getTotalUptime());
+
+ timeInterface.incrementTime(5000).tick();
+ assertEquals(10000, uptimeTracker.getTotalUptime());
+
+ timeInterface.incrementTime(500).tick();
+ uptimeTracker.onDestroy();
+ timeInterface.cancelAllActions();
+
+ uptimeTracker = new UptimeTracker(uptimeFile.getFile(),
+ SNAPSHOT_INTERVAL, timeInterface);
+
+ timeInterface.incrementTime(3000).tick();
+ assertEquals(13500, uptimeTracker.getTotalUptime());
+ }
+ }
+
+ public void testUptimeTrackerShutdown() throws Exception {
+ MockTimeInterface timeInterface = new MockTimeInterface();
+ try (TemporaryFile uptimeFile = new TemporaryFile(TAG)) {
+ UptimeTracker uptimeTracker = new UptimeTracker(uptimeFile.getFile(),
+ SNAPSHOT_INTERVAL, timeInterface);
+
+ timeInterface.incrementTime(6000);
+ uptimeTracker.onDestroy();
+ timeInterface.cancelAllActions();
+
+ uptimeTracker = new UptimeTracker(uptimeFile.getFile(),
+ SNAPSHOT_INTERVAL, timeInterface);
+ assertEquals(6000, uptimeTracker.getTotalUptime());
+ }
+ }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/VmsLayersAvailabilityTest.java b/tests/carservice_unit_test/src/com/android/car/VmsLayersAvailabilityTest.java
new file mode 100644
index 0000000..4a09d4f
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/VmsLayersAvailabilityTest.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.android.car;
+
+import android.car.vms.VmsAssociatedLayer;
+import android.car.vms.VmsLayer;
+import android.car.vms.VmsLayerDependency;
+import android.car.vms.VmsLayersOffering;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+@SmallTest
+public class VmsLayersAvailabilityTest extends AndroidTestCase {
+
+ 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)));
+
+ private static final VmsLayerDependency X_DEPENDS_ON_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)));
+
+ private static final VmsLayerDependency Y_DEPENDS_ON_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)));
+
+ private static final VmsLayerDependency Z_DEPENDS_ON_NOTHING =
+ new VmsLayerDependency(LAYER_Z);
+
+ private static final VmsLayerDependency X_DEPENDS_ON_SELF =
+ new VmsLayerDependency(LAYER_X, new HashSet<VmsLayer>(Arrays.asList(LAYER_X)));
+
+ private Set<VmsLayersOffering> mOfferings;
+ private VmsLayersAvailability mLayersAvailability;
+
+ @Override
+ protected void setUp() throws Exception {
+ mLayersAvailability = new VmsLayersAvailability();
+ mOfferings = new HashSet<>();
+ super.setUp();
+ }
+
+ public void testNoOffering() {
+ assertTrue(mLayersAvailability.getAvailableLayers().isEmpty());
+ }
+
+ public void testEmptyOffering() {
+ mLayersAvailability.setPublishersOffering(Collections.EMPTY_LIST);
+ assertTrue(mLayersAvailability.getAvailableLayers().isEmpty());
+ }
+
+ public void testSingleLayerNoDeps() throws Exception {
+ Set<VmsAssociatedLayer> expectedAvailableAssociatedLayers = new HashSet<>();
+ expectedAvailableAssociatedLayers.add(new VmsAssociatedLayer(LAYER_X, PUBLISHERS_2));
+
+ VmsLayersOffering offering =
+ new VmsLayersOffering(new HashSet<>(Arrays.asList(new VmsLayerDependency(LAYER_X))),
+ PUBLISHER_ID_2);
+
+ mOfferings.add(offering);
+ mLayersAvailability.setPublishersOffering(mOfferings);
+
+ assertEquals(expectedAvailableAssociatedLayers, mLayersAvailability.getAvailableLayers());
+ }
+
+ public void testChainOfDependenciesSatisfied() throws Exception {
+ 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(
+ 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(expectedAvailableAssociatedLayers,
+ new HashSet<VmsAssociatedLayer>(mLayersAvailability.getAvailableLayers()));
+ }
+
+ public void testChainOfDependenciesSatisfiedTwoOfferings() throws Exception {
+ 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(
+ new HashSet<>(Arrays.asList(X_DEPENDS_ON_Y, Y_DEPENDS_ON_Z)),
+ PUBLISHER_ID_1);
+
+ VmsLayersOffering offering2 =
+ new VmsLayersOffering(new HashSet<>(Arrays.asList(Z_DEPENDS_ON_NOTHING)),
+ PUBLISHER_ID_1);
+
+ mOfferings.add(offering1);
+ mOfferings.add(offering2);
+ mLayersAvailability.setPublishersOffering(mOfferings);
+
+ assertEquals(expectedAvailableAssociatedLayers,
+ new HashSet<VmsAssociatedLayer>(mLayersAvailability.getAvailableLayers()));
+ }
+
+ public void testChainOfDependencieNotSatisfied() throws Exception {
+ 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(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_1));
+
+
+ assertEquals(expectedUnavailableAssociatedLayers,
+ new HashSet<VmsAssociatedLayer>(mLayersAvailability.getUnavailableLayers()));
+ }
+
+ public void testOneOfMultipleDependencySatisfied() throws Exception {
+ 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(
+ 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(expectedAvailableAssociatedLayers,
+ new HashSet<VmsAssociatedLayer>(mLayersAvailability.getAvailableLayers()));
+ }
+
+ public void testCyclicDependency() throws Exception {
+ Set<VmsAssociatedLayer> expectedAvailableAssociatedLayers = new HashSet<>();
+
+ VmsLayersOffering offering =
+ 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(expectedAvailableAssociatedLayers,
+ new HashSet<VmsAssociatedLayer>(mLayersAvailability.getAvailableLayers()));
+ }
+
+ public void testAlmostCyclicDependency() throws Exception {
+ 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(
+ new HashSet<>(Arrays.asList(X_DEPENDS_ON_Y, Z_DEPENDS_ON_NOTHING)),
+ PUBLISHER_ID_1);
+
+ VmsLayersOffering offering2 =
+ 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(expectedAvailableAssociatedLayers, mLayersAvailability.getAvailableLayers());
+ }
+
+ public void testCyclicDependencyAndLayerWithoutDependency() throws Exception {
+ Set<VmsAssociatedLayer> expectedAvailableAssociatedLayers = new HashSet<>();
+ expectedAvailableAssociatedLayers.add(new VmsAssociatedLayer(LAYER_Z, PUBLISHERS_1));
+
+ VmsLayersOffering offering1 =
+ new VmsLayersOffering(
+ new HashSet<>(
+ Arrays.asList(X_DEPENDS_ON_Y, Z_DEPENDS_ON_NOTHING)),
+ PUBLISHER_ID_1);
+
+ VmsLayersOffering offering2 =
+ new VmsLayersOffering(new HashSet<>(Arrays.asList(Y_DEPENDS_ON_X)), PUBLISHER_ID_2);
+
+ mOfferings.add(offering1);
+ mOfferings.add(offering2);
+ mLayersAvailability.setPublishersOffering(mOfferings);
+
+ 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));
+
+ assertEquals(expectedUnavailableAssociatedLayers,
+ new HashSet<VmsAssociatedLayer>(mLayersAvailability.getUnavailableLayers()));
+ }
+
+ public void testSelfDependency() throws Exception {
+ Set<VmsAssociatedLayer> expectedAvailableAssociatedLayers = new HashSet<>();
+
+ VmsLayersOffering offering =
+ new VmsLayersOffering(new HashSet<>(Arrays.asList(X_DEPENDS_ON_SELF)),
+ PUBLISHER_ID_1);
+
+ mOfferings.add(offering);
+ mLayersAvailability.setPublishersOffering(mOfferings);
+
+ assertEquals(expectedAvailableAssociatedLayers,
+ new HashSet<VmsAssociatedLayer>(mLayersAvailability.getAvailableLayers()));
+
+ Set<VmsAssociatedLayer> expectedUnavailableAssociatedLayers = new HashSet<>();
+ expectedUnavailableAssociatedLayers.add(new VmsAssociatedLayer(LAYER_X, PUBLISHERS_1));
+
+ assertEquals(expectedUnavailableAssociatedLayers,
+ new HashSet<VmsAssociatedLayer>(mLayersAvailability.getUnavailableLayers()));
+ }
+}
\ No newline at end of file
diff --git a/tests/carservice_unit_test/src/com/android/car/VmsPublishersInfoTest.java b/tests/carservice_unit_test/src/com/android/car/VmsPublishersInfoTest.java
new file mode 100644
index 0000000..3f922c9
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/VmsPublishersInfoTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.Arrays;
+import java.util.Map;
+
+@SmallTest
+public class VmsPublishersInfoTest extends AndroidTestCase {
+ public static final byte[] MOCK_INFO_0 = new byte[]{2, 3, 5, 7, 11, 13, 17};
+ public static final byte[] SAME_MOCK_INFO_0 = new byte[]{2, 3, 5, 7, 11, 13, 17};
+ public static final byte[] MOCK_INFO_1 = new byte[]{2, 3, 5, 7, 11, 13, 17, 19};
+
+ private VmsPublishersInfo mVmsPublishersInfo;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mVmsPublishersInfo = new VmsPublishersInfo();
+ }
+
+ // Test one info sanity
+ public void testSingleInfo() throws Exception {
+ int id = mVmsPublishersInfo.getIdForInfo(MOCK_INFO_0);
+ assertEquals(0, id);
+
+ byte[] info = mVmsPublishersInfo.getPublisherInfo(id);
+ assertTrue(Arrays.equals(MOCK_INFO_0, info));
+ }
+
+ // Test one info sanity - wrong ID fails.
+ public void testSingleInfoWrongId() throws Exception {
+ int id = mVmsPublishersInfo.getIdForInfo(MOCK_INFO_0);
+ assertEquals(0, id);
+
+ try {
+ byte[] info = mVmsPublishersInfo.getPublisherInfo(id + 1);
+ }
+ catch (NullPointerException e) {
+ return;
+ }
+ fail();
+ }
+
+ // Test two infos.
+ public void testTwoInfos() throws Exception {
+ int id0 = mVmsPublishersInfo.getIdForInfo(MOCK_INFO_0);
+ int id1 = mVmsPublishersInfo.getIdForInfo(MOCK_INFO_1);
+ assertEquals(0, id0);
+ assertEquals(1, id1);
+
+ byte[] info0 = mVmsPublishersInfo.getPublisherInfo(id0);
+ byte[] info1 = mVmsPublishersInfo.getPublisherInfo(id1);
+ assertTrue(Arrays.equals(MOCK_INFO_0, info0));
+ assertTrue(Arrays.equals(MOCK_INFO_1, info1));
+ }
+
+ // Test same info twice get the same ID.
+ public void testSingleInfoInsertedTwice() throws Exception {
+ int id = mVmsPublishersInfo.getIdForInfo(MOCK_INFO_0);
+ assertEquals(0, id);
+
+ int sameId = mVmsPublishersInfo.getIdForInfo(SAME_MOCK_INFO_0);
+ assertEquals(sameId, id);
+ }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/VmsRoutingTest.java b/tests/carservice_unit_test/src/com/android/car/VmsRoutingTest.java
new file mode 100644
index 0000000..8fe849b
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/VmsRoutingTest.java
@@ -0,0 +1,309 @@
+/*
+ * 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.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, 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
+ protected void setUp() throws Exception {
+ super.setUp();
+ mRouting = new VmsRouting();
+ }
+
+ public void testAddingSubscribersAndHalLayersNoOverlap() 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);
+
+ // Verify expected subscriptions are in routing manager.
+ Set<VmsLayer> expectedSubscriptions = new HashSet<>();
+ expectedSubscriptions.add(LAYER_WITH_SUBSCRIPTION_1);
+ expectedSubscriptions.add(LAYER_WITH_SUBSCRIPTION_2);
+ VmsSubscriptionState subscriptionState = mRouting.getSubscriptionState();
+ assertEquals(2, subscriptionState.getSequenceNumber());
+ assertEquals(expectedSubscriptions,
+ new HashSet<>(subscriptionState.getLayers()));
+
+ // Verify there is only a single subscriber.
+ assertEquals(1,
+ mRouting.getSubscribersForLayerFromPublisher(
+ LAYER_WITH_SUBSCRIPTION_1, PUBLISHER_ID_1).size());
+ }
+
+ public void testAddingSubscribersAndHalLayersWithOverlap() throws Exception {
+ // Add a subscription to a layer.
+ 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);
+
+ // Verify expected subscriptions are in routing manager.
+ Set<VmsLayer> expectedSubscriptions = new HashSet<>();
+ expectedSubscriptions.add(LAYER_WITH_SUBSCRIPTION_1);
+ expectedSubscriptions.add(LAYER_WITH_SUBSCRIPTION_2);
+ VmsSubscriptionState subscriptionState = mRouting.getSubscriptionState();
+ assertEquals(3, subscriptionState.getSequenceNumber());
+ assertEquals(expectedSubscriptions,
+ new HashSet<>(subscriptionState.getLayers()));
+ }
+
+ public void testAddingAndRemovingLayers() 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);
+
+ // Remove a subscription to a layer.
+ mRouting.removeSubscription(subscriber, LAYER_WITH_SUBSCRIPTION_1);
+
+ // Update the HAL subscription
+ mRouting.removeHalSubscription(LAYER_WITH_SUBSCRIPTION_2);
+
+ // Verify there are no subscribers in the routing manager.
+ VmsSubscriptionState subscriptionState = mRouting.getSubscriptionState();
+ assertEquals(4, subscriptionState.getSequenceNumber());
+ assertTrue(subscriptionState.getLayers().isEmpty());
+ }
+
+ public void testAddingBothTypesOfSubscribers() throws Exception {
+ // Add a subscription to a layer.
+ MockVmsSubscriber subscriberForLayer = new MockVmsSubscriber();
+ mRouting.addSubscription(subscriberForLayer, LAYER_WITH_SUBSCRIPTION_1);
+
+ // Add a subscription without a layer.
+ MockVmsSubscriber subscriberWithoutLayer = new MockVmsSubscriber();
+ mRouting.addSubscription(subscriberWithoutLayer);
+
+ // Verify 2 subscribers for the layer.
+ assertEquals(2,
+ mRouting.getSubscribersForLayerFromPublisher(
+ LAYER_WITH_SUBSCRIPTION_1, PUBLISHER_ID_1).size());
+
+ // Add the subscriber with layer as also a subscriber without layer
+ mRouting.addSubscription(subscriberForLayer);
+
+ // The number of subscribers for the layer should remain the same as before.
+ assertEquals(2,
+ mRouting.getSubscribersForLayerFromPublisher(
+ LAYER_WITH_SUBSCRIPTION_1, PUBLISHER_ID_1).size());
+ }
+
+ public void testOnlyRelevantSubscribers() throws Exception {
+ // Add a subscription to a layer.
+ MockVmsSubscriber subscriberForLayer = new MockVmsSubscriber();
+ mRouting.addSubscription(subscriberForLayer, LAYER_WITH_SUBSCRIPTION_1);
+
+ // Add a subscription without a layer.
+ MockVmsSubscriber subscriberWithoutLayer = new MockVmsSubscriber();
+ mRouting.addSubscription(subscriberWithoutLayer);
+
+ // Verify that only the subscriber without layer is returned.
+ Set<MockVmsSubscriber> expectedListeneres = new HashSet<MockVmsSubscriber>();
+ expectedListeneres.add(subscriberWithoutLayer);
+ assertEquals(expectedListeneres,
+ mRouting.getSubscribersForLayerFromPublisher(
+ LAYER_WITHOUT_SUBSCRIPTION, PUBLISHER_ID_1));
+ }
+
+ 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) {
+ }
+
+ @Override
+ public void onLayersAvailabilityChanged(List<VmsAssociatedLayer> availableLayers) {
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/carservice_unit_test/src/com/android/car/storagemonitoring/CarStorageMonitoringTest.java b/tests/carservice_unit_test/src/com/android/car/storagemonitoring/CarStorageMonitoringTest.java
new file mode 100644
index 0000000..4e98a10
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/storagemonitoring/CarStorageMonitoringTest.java
@@ -0,0 +1,479 @@
+/*
+ * 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.storagemonitoring;
+
+import android.car.storagemonitoring.UidIoStats;
+import android.car.storagemonitoring.UidIoStats.PerStateMetrics;
+import android.car.storagemonitoring.UidIoStatsRecord;
+import android.car.storagemonitoring.WearEstimate;
+import android.car.storagemonitoring.WearEstimateChange;
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import android.util.SparseArray;
+import com.android.car.test.utils.TemporaryFile;
+import android.util.JsonReader;
+import android.util.JsonWriter;
+
+import java.io.FileWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.nio.file.Files;
+import java.time.Instant;
+import java.util.List;
+import junit.framework.TestCase;
+import org.json.JSONObject;
+
+/**
+ * Tests the storage monitoring API in CarService.
+ */
+@MediumTest
+public class CarStorageMonitoringTest extends TestCase {
+ static final String TAG = CarStorageMonitoringTest.class.getSimpleName();
+
+ public void testEMmcWearInformationProvider() throws Exception {
+ try (TemporaryFile lifetimeFile = new TemporaryFile(TAG)) {
+ try (TemporaryFile eolFile = new TemporaryFile(TAG)) {
+ lifetimeFile.write("0x05 0x00");
+ eolFile.write("01");
+
+ EMmcWearInformationProvider wearInfoProvider = new EMmcWearInformationProvider(
+ lifetimeFile.getFile(), eolFile.getFile());
+
+ WearInformation wearInformation = wearInfoProvider.load();
+
+ assertNotNull(wearInformation);
+ assertEquals(40, wearInformation.lifetimeEstimateA);
+ assertEquals(WearInformation.UNKNOWN_LIFETIME_ESTIMATE,
+ wearInformation.lifetimeEstimateB);
+
+ assertEquals(WearInformation.PRE_EOL_INFO_NORMAL, wearInformation.preEolInfo);
+ }
+ }
+ }
+
+ public void testUfsWearInformationProvider() throws Exception {
+ try (TemporaryFile lifetimeFile = new TemporaryFile(TAG)) {
+ lifetimeFile.write("ufs version: 1.0\n" +
+ "Health Descriptor[Byte offset 0x2]: bPreEOLInfo = 0x2\n" +
+ "Health Descriptor[Byte offset 0x1]: bDescriptionIDN = 0x1\n" +
+ "Health Descriptor[Byte offset 0x3]: bDeviceLifeTimeEstA = 0x0\n" +
+ "Health Descriptor[Byte offset 0x5]: VendorPropInfo = somedatahere\n" +
+ "Health Descriptor[Byte offset 0x4]: bDeviceLifeTimeEstB = 0xA\n");
+
+ UfsWearInformationProvider wearInfoProvider = new UfsWearInformationProvider(
+ lifetimeFile.getFile());
+
+ WearInformation wearInformation = wearInfoProvider.load();
+
+ assertNotNull(wearInformation);
+ assertEquals(90, wearInformation.lifetimeEstimateB);
+ assertEquals(WearInformation.PRE_EOL_INFO_WARNING, wearInformation.preEolInfo);
+ assertEquals(WearInformation.UNKNOWN_LIFETIME_ESTIMATE,
+ wearInformation.lifetimeEstimateA);
+ }
+ }
+
+ public void testWearEstimateEquality() {
+ WearEstimate wearEstimate1 = new WearEstimate(10, 20);
+ WearEstimate wearEstimate2 = new WearEstimate(10, 20);
+ WearEstimate wearEstimate3 = new WearEstimate(20, 30);
+ assertEquals(wearEstimate1, wearEstimate1);
+ assertEquals(wearEstimate1, wearEstimate2);
+ assertNotSame(wearEstimate1, wearEstimate3);
+ }
+
+ public void testWearEstimateParcel() throws Exception {
+ WearEstimate originalWearEstimate = new WearEstimate(10, 20);
+ Parcel p = Parcel.obtain();
+ originalWearEstimate.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ WearEstimate newWearEstimate = new WearEstimate(p);
+ assertEquals(originalWearEstimate, newWearEstimate);
+ p.recycle();
+ }
+
+ public void testWearEstimateChangeEquality() {
+ WearEstimateChange wearEstimateChange1 = new WearEstimateChange(
+ new WearEstimate(10, 20),
+ new WearEstimate(20, 30),
+ 5000L,
+ Instant.now(),
+ false);
+ WearEstimateChange wearEstimateChange2 = new WearEstimateChange(
+ new WearEstimate(10, 20),
+ new WearEstimate(20, 30),
+ 5000L,
+ wearEstimateChange1.dateAtChange,
+ false);
+ assertEquals(wearEstimateChange1, wearEstimateChange1);
+ assertEquals(wearEstimateChange1, wearEstimateChange2);
+ WearEstimateChange wearEstimateChange3 = new WearEstimateChange(
+ new WearEstimate(10, 30),
+ new WearEstimate(20, 30),
+ 3000L,
+ Instant.now(),
+ true);
+ assertNotSame(wearEstimateChange1, wearEstimateChange3);
+ }
+
+ public void testWearEstimateChangeParcel() throws Exception {
+ WearEstimateChange originalWearEstimateChange = new WearEstimateChange(
+ new WearEstimate(10, 0),
+ new WearEstimate(20, 10),
+ 123456789L,
+ Instant.now(),
+ false);
+ Parcel p = Parcel.obtain();
+ originalWearEstimateChange.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ WearEstimateChange newWearEstimateChange = new WearEstimateChange(p);
+ assertEquals(originalWearEstimateChange, newWearEstimateChange);
+ p.recycle();
+ }
+
+ public void testWearEstimateJson() throws Exception {
+ WearEstimate originalWearEstimate = new WearEstimate(20, 0);
+ StringWriter stringWriter = new StringWriter(1024);
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ originalWearEstimate.writeToJson(jsonWriter);
+ StringReader stringReader = new StringReader(stringWriter.toString());
+ JsonReader jsonReader = new JsonReader(stringReader);
+ WearEstimate newWearEstimate = new WearEstimate(jsonReader);
+ assertEquals(originalWearEstimate, newWearEstimate);
+ }
+
+ public void testWearEstimateRecordJson() throws Exception {
+ try (TemporaryFile temporaryFile = new TemporaryFile(TAG)) {
+ WearEstimateRecord originalWearEstimateRecord = new WearEstimateRecord(new WearEstimate(10, 20),
+ new WearEstimate(10, 30), 5000, Instant.ofEpochMilli(1000));
+ try (JsonWriter jsonWriter = new JsonWriter(new FileWriter(temporaryFile.getFile()))) {
+ originalWearEstimateRecord.writeToJson(jsonWriter);
+ }
+ JSONObject jsonObject = new JSONObject(
+ new String(Files.readAllBytes(temporaryFile.getPath())));
+ WearEstimateRecord newWearEstimateRecord = new WearEstimateRecord(jsonObject);
+ assertEquals(originalWearEstimateRecord, newWearEstimateRecord);
+ }
+ }
+
+ public void testWearEstimateRecordEquality() throws Exception {
+ WearEstimateRecord wearEstimateRecord1 = new WearEstimateRecord(WearEstimate.UNKNOWN_ESTIMATE,
+ new WearEstimate(10, 20), 5000, Instant.ofEpochMilli(2000));
+ WearEstimateRecord wearEstimateRecord2 = new WearEstimateRecord(WearEstimate.UNKNOWN_ESTIMATE,
+ new WearEstimate(10, 20), 5000, Instant.ofEpochMilli(2000));
+ WearEstimateRecord wearEstimateRecord3 = new WearEstimateRecord(WearEstimate.UNKNOWN_ESTIMATE,
+ new WearEstimate(10, 40), 5000, Instant.ofEpochMilli(1000));
+
+ assertEquals(wearEstimateRecord1, wearEstimateRecord1);
+ assertEquals(wearEstimateRecord1, wearEstimateRecord2);
+ assertNotSame(wearEstimateRecord1, wearEstimateRecord3);
+ }
+
+ public void testWearHistoryJson() throws Exception {
+ try (TemporaryFile temporaryFile = new TemporaryFile(TAG)) {
+ WearEstimateRecord wearEstimateRecord1 = new WearEstimateRecord(
+ WearEstimate.UNKNOWN_ESTIMATE,
+ new WearEstimate(10, 20), 5000, Instant.ofEpochMilli(2000));
+ WearEstimateRecord wearEstimateRecord2 = new WearEstimateRecord(
+ wearEstimateRecord1.getOldWearEstimate(),
+ new WearEstimate(10, 40), 9000, Instant.ofEpochMilli(16000));
+ WearEstimateRecord wearEstimateRecord3 = new WearEstimateRecord(
+ wearEstimateRecord2.getOldWearEstimate(),
+ new WearEstimate(20, 40), 12000, Instant.ofEpochMilli(21000));
+ WearHistory originalWearHistory = WearHistory.fromRecords(wearEstimateRecord1,
+ wearEstimateRecord2, wearEstimateRecord3);
+ try (JsonWriter jsonWriter = new JsonWriter(new FileWriter(temporaryFile.getFile()))) {
+ originalWearHistory.writeToJson(jsonWriter);
+ }
+ JSONObject jsonObject = new JSONObject(
+ new String(Files.readAllBytes(temporaryFile.getPath())));
+ WearHistory newWearHistory = new WearHistory(jsonObject);
+ assertEquals(originalWearHistory, newWearHistory);
+ }
+ }
+
+ public void testWearHistoryEquality() throws Exception {
+ WearEstimateRecord wearEstimateRecord1 = new WearEstimateRecord(
+ WearEstimate.UNKNOWN_ESTIMATE,
+ new WearEstimate(10, 20), 5000, Instant.ofEpochMilli(2000));
+ WearEstimateRecord wearEstimateRecord2 = new WearEstimateRecord(
+ wearEstimateRecord1.getOldWearEstimate(),
+ new WearEstimate(10, 40), 9000, Instant.ofEpochMilli(16000));
+ WearEstimateRecord wearEstimateRecord3 = new WearEstimateRecord(
+ wearEstimateRecord2.getOldWearEstimate(),
+ new WearEstimate(20, 40), 12000, Instant.ofEpochMilli(21000));
+ WearEstimateRecord wearEstimateRecord4 = new WearEstimateRecord(
+ wearEstimateRecord3.getOldWearEstimate(),
+ new WearEstimate(30, 50), 17000, Instant.ofEpochMilli(34000));
+ WearEstimateRecord wearEstimateRecord5 = new WearEstimateRecord(
+ wearEstimateRecord3.getOldWearEstimate(),
+ new WearEstimate(20, 50), 15000, Instant.ofEpochMilli(34000));
+
+ WearHistory wearHistory1 = WearHistory.fromRecords(wearEstimateRecord1,
+ wearEstimateRecord2, wearEstimateRecord3, wearEstimateRecord4);
+ WearHistory wearHistory2 = WearHistory.fromRecords(wearEstimateRecord4,
+ wearEstimateRecord1, wearEstimateRecord2, wearEstimateRecord3);
+ WearHistory wearHistory3 = WearHistory.fromRecords(wearEstimateRecord1,
+ wearEstimateRecord2, wearEstimateRecord3, wearEstimateRecord5);
+
+ assertEquals(wearHistory1, wearHistory1);
+ assertEquals(wearHistory1, wearHistory2);
+ assertNotSame(wearHistory1, wearHistory3);
+ }
+
+ public void testWearHistoryToChanges() {
+ WearEstimateRecord wearEstimateRecord1 = new WearEstimateRecord(
+ WearEstimate.UNKNOWN_ESTIMATE,
+ new WearEstimate(10, 20), 3600000, Instant.ofEpochMilli(2000));
+ WearEstimateRecord wearEstimateRecord2 = new WearEstimateRecord(
+ wearEstimateRecord1.getOldWearEstimate(),
+ new WearEstimate(10, 40), 172000000, Instant.ofEpochMilli(16000));
+ WearEstimateRecord wearEstimateRecord3 = new WearEstimateRecord(
+ wearEstimateRecord2.getOldWearEstimate(),
+ new WearEstimate(20, 40), 172000001, Instant.ofEpochMilli(21000));
+
+ WearHistory wearHistory = WearHistory.fromRecords(wearEstimateRecord1,
+ wearEstimateRecord2, wearEstimateRecord3);
+
+ List<WearEstimateChange> wearEstimateChanges = wearHistory.toWearEstimateChanges(1);
+
+ assertEquals(3, wearEstimateChanges.size());
+ WearEstimateChange unknownToOne = wearEstimateChanges.get(0);
+ WearEstimateChange oneToTwo = wearEstimateChanges.get(1);
+ WearEstimateChange twoToThree = wearEstimateChanges.get(2);
+
+ assertEquals(unknownToOne.oldEstimate, wearEstimateRecord1.getOldWearEstimate());
+ assertEquals(unknownToOne.newEstimate, wearEstimateRecord1.getNewWearEstimate());
+ assertEquals(unknownToOne.uptimeAtChange, wearEstimateRecord1.getTotalCarServiceUptime());
+ assertEquals(unknownToOne.dateAtChange, wearEstimateRecord1.getUnixTimestamp());
+ assertTrue(unknownToOne.isAcceptableDegradation);
+
+ assertEquals(oneToTwo.oldEstimate, wearEstimateRecord2.getOldWearEstimate());
+ assertEquals(oneToTwo.newEstimate, wearEstimateRecord2.getNewWearEstimate());
+ assertEquals(oneToTwo.uptimeAtChange, wearEstimateRecord2.getTotalCarServiceUptime());
+ assertEquals(oneToTwo.dateAtChange, wearEstimateRecord2.getUnixTimestamp());
+ assertTrue(oneToTwo.isAcceptableDegradation);
+
+ assertEquals(twoToThree.oldEstimate, wearEstimateRecord3.getOldWearEstimate());
+ assertEquals(twoToThree.newEstimate, wearEstimateRecord3.getNewWearEstimate());
+ assertEquals(twoToThree.uptimeAtChange, wearEstimateRecord3.getTotalCarServiceUptime());
+ assertEquals(twoToThree.dateAtChange, wearEstimateRecord3.getUnixTimestamp());
+ assertFalse(twoToThree.isAcceptableDegradation);
+ }
+
+ public void testUidIoStatEntry() throws Exception {
+ try (TemporaryFile statsFile = new TemporaryFile(TAG)) {
+ statsFile.write("0 256797495 181736102 362132480 947167232 0 0 0 0 250 0\n"
+ + "1006 489007 196802 0 20480 51474 2048 1024 2048 1 1\n");
+
+ ProcfsUidIoStatsProvider statsProvider = new ProcfsUidIoStatsProvider(
+ statsFile.getPath());
+
+ SparseArray<UidIoStatsRecord> entries = statsProvider.load();
+
+ assertNotNull(entries);
+ assertEquals(2, entries.size());
+
+ UidIoStats entry = new UidIoStats(entries.get(0), 1234);
+ assertNotNull(entry);
+ assertEquals(0, entry.uid);
+ assertEquals(1234, entry.runtimeMillis);
+ assertEquals(256797495, entry.foreground.bytesRead);
+ assertEquals(181736102, entry.foreground.bytesWritten);
+ assertEquals(362132480, entry.foreground.bytesReadFromStorage);
+ assertEquals(947167232, entry.foreground.bytesWrittenToStorage);
+ assertEquals(250, entry.foreground.fsyncCalls);
+ assertEquals(0, entry.background.bytesRead);
+ assertEquals(0, entry.background.bytesWritten);
+ assertEquals(0, entry.background.bytesReadFromStorage);
+ assertEquals(0, entry.background.bytesWrittenToStorage);
+ assertEquals(0, entry.background.fsyncCalls);
+
+ entry = new UidIoStats(entries.get(1006), 4321);
+ assertNotNull(entry);
+ assertEquals(1006, entry.uid);
+ assertEquals(4321, entry.runtimeMillis);
+ assertEquals(489007, entry.foreground.bytesRead);
+ assertEquals(196802, entry.foreground.bytesWritten);
+ assertEquals(0, entry.foreground.bytesReadFromStorage);
+ assertEquals(20480, entry.foreground.bytesWrittenToStorage);
+ assertEquals(1, entry.foreground.fsyncCalls);
+ assertEquals(51474, entry.background.bytesRead);
+ assertEquals(2048, entry.background.bytesWritten);
+ assertEquals(1024, entry.background.bytesReadFromStorage);
+ assertEquals(2048, entry.background.bytesWrittenToStorage);
+ assertEquals(1, entry.background.fsyncCalls);
+ }
+ }
+
+ public void testUidIoStatEntryMissingFields() throws Exception {
+ try (TemporaryFile statsFile = new TemporaryFile(TAG)) {
+ statsFile.write("0 256797495 181736102 362132480 947167232 0 0 0 0 250 0\n"
+ + "1 2 3 4 5 6 7 8 9\n");
+
+ ProcfsUidIoStatsProvider statsProvider = new ProcfsUidIoStatsProvider(
+ statsFile.getPath());
+
+ SparseArray<UidIoStatsRecord> entries = statsProvider.load();
+
+ assertNull(entries);
+ }
+ }
+
+ public void testUidIoStatEntryNonNumericFields() throws Exception {
+ try (TemporaryFile statsFile = new TemporaryFile(TAG)) {
+ statsFile.write("0 256797495 181736102 362132480 947167232 0 0 0 0 250 0\n"
+ + "notanumber 489007 196802 0 20480 51474 2048 1024 2048 1 1\n");
+
+ ProcfsUidIoStatsProvider statsProvider = new ProcfsUidIoStatsProvider(
+ statsFile.getPath());
+
+ SparseArray<UidIoStatsRecord> entries = statsProvider.load();
+
+ assertNull(entries);
+ }
+ }
+
+ public void testUidIoStatEntryEquality() throws Exception {
+ UidIoStats statEntry1 = new UidIoStats(10, 1234,
+ new PerStateMetrics(10, 20, 30, 40, 50),
+ new PerStateMetrics(100, 200, 300, 400, 500));
+ UidIoStats statEntry2 = new UidIoStats(10, 1234,
+ new PerStateMetrics(10, 20, 30, 40, 50),
+ new PerStateMetrics(100, 200, 300, 400, 500));
+ UidIoStats statEntry3 = new UidIoStats(30, 4567,
+ new PerStateMetrics(1, 20, 30, 42, 50),
+ new PerStateMetrics(10, 200, 300, 420, 500));
+ UidIoStats statEntry4 = new UidIoStats(11, 6541,
+ new PerStateMetrics(10, 20, 30, 40, 50),
+ new PerStateMetrics(100, 200, 300, 400, 500));
+ UidIoStats statEntry5 = new UidIoStats(10, 1234,
+ new PerStateMetrics(10, 20, 30, 40, 0),
+ new PerStateMetrics(100, 200, 300, 400, 500));
+
+ assertEquals(statEntry1, statEntry1);
+ assertEquals(statEntry1, statEntry2);
+ assertNotSame(statEntry1, statEntry3);
+ assertNotSame(statEntry1, statEntry4);
+ assertNotSame(statEntry1, statEntry5);
+ }
+
+ public void testUidIoStatEntryParcel() throws Exception {
+ UidIoStats statEntry = new UidIoStats(10, 5000,
+ new PerStateMetrics(10, 20, 30, 40, 50),
+ new PerStateMetrics(100, 200, 300, 400, 500));
+ Parcel p = Parcel.obtain();
+ statEntry.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ UidIoStats other = new UidIoStats(p);
+ assertEquals(other, statEntry);
+ }
+
+ public void testUidIoStatEntryJson() throws Exception {
+ try (TemporaryFile temporaryFile = new TemporaryFile(TAG)) {
+ UidIoStats statEntry = new UidIoStats(10, 1200,
+ new PerStateMetrics(10, 20, 30, 40, 50),
+ new PerStateMetrics(100, 200, 300, 400, 500));
+ try (JsonWriter jsonWriter = new JsonWriter(new FileWriter(temporaryFile.getFile()))) {
+ statEntry.writeToJson(jsonWriter);
+ }
+ JSONObject jsonObject = new JSONObject(
+ new String(Files.readAllBytes(temporaryFile.getPath())));
+ UidIoStats other = new UidIoStats(jsonObject);
+ assertEquals(statEntry, other);
+ }
+ }
+
+
+ public void testUidIoStatEntryDelta() throws Exception {
+ UidIoStats statEntry1 = new UidIoStats(10, 1000,
+ new PerStateMetrics(10, 20, 30, 40, 50),
+ new PerStateMetrics(60, 70, 80, 90, 100));
+
+ UidIoStats statEntry2 = new UidIoStats(10,2000,
+ new PerStateMetrics(110, 120, 130, 140, 150),
+ new PerStateMetrics(260, 370, 480, 500, 110));
+
+ UidIoStats statEntry3 = new UidIoStats(30, 3000,
+ new PerStateMetrics(10, 20, 30, 40, 50),
+ new PerStateMetrics(100, 200, 300, 400, 500));
+
+
+ UidIoStats delta21 = statEntry2.delta(statEntry1);
+ assertNotNull(delta21);
+ assertEquals(statEntry1.uid, delta21.uid);
+
+ assertEquals(1000, delta21.runtimeMillis);
+ assertEquals(100, delta21.foreground.bytesRead);
+ assertEquals(100, delta21.foreground.bytesWritten);
+ assertEquals(100, delta21.foreground.bytesReadFromStorage);
+ assertEquals(100, delta21.foreground.bytesWrittenToStorage);
+ assertEquals(100, delta21.foreground.fsyncCalls);
+
+ assertEquals(200, delta21.background.bytesRead);
+ assertEquals(300, delta21.background.bytesWritten);
+ assertEquals(400, delta21.background.bytesReadFromStorage);
+ assertEquals(410, delta21.background.bytesWrittenToStorage);
+ assertEquals(10, delta21.background.fsyncCalls);
+
+ try {
+ UidIoStats delta31 = statEntry3.delta(statEntry1);
+ fail("delta only allowed on stats for matching user ID");
+ } catch (IllegalArgumentException e) {
+ // test passed
+ }
+ }
+
+ public void testUidIoStatsRecordDelta() throws Exception {
+ UidIoStats statEntry = new UidIoStats(10, 1000,
+ new PerStateMetrics(10, 20, 30, 40, 50),
+ new PerStateMetrics(60, 70, 80, 90, 100));
+
+ UidIoStatsRecord statRecord = new UidIoStatsRecord(10,
+ 20, 20, 30, 50, 70,
+ 80, 70, 80, 100, 110);
+
+ UidIoStatsRecord delta = statRecord.delta(statEntry);
+
+ assertNotNull(delta);
+ assertEquals(statRecord.uid, delta.uid);
+
+ assertEquals(10, delta.foreground_rchar);
+ assertEquals(0, delta.foreground_wchar);
+ assertEquals(0, delta.foreground_read_bytes);
+ assertEquals(10, delta.foreground_write_bytes);
+ assertEquals(20, delta.foreground_fsync);
+
+ assertEquals(20, delta.background_rchar);
+ assertEquals(0, delta.background_wchar);
+ assertEquals(0, delta.background_read_bytes);
+ assertEquals(10, delta.background_write_bytes);
+ assertEquals(10, delta.background_fsync);
+
+ statRecord = new UidIoStatsRecord(30,
+ 20, 20, 30, 50, 70,
+ 80, 70, 80, 100, 110);
+
+ try {
+ statRecord.delta(statEntry);
+ fail("delta only allowed on records for matching user ID");
+ } catch (IllegalArgumentException e) {
+ // test passed
+ }
+ }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/storagemonitoring/IoStatsTrackerTest.java b/tests/carservice_unit_test/src/com/android/car/storagemonitoring/IoStatsTrackerTest.java
new file mode 100644
index 0000000..c694f50
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/storagemonitoring/IoStatsTrackerTest.java
@@ -0,0 +1,342 @@
+/*
+ * 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.storagemonitoring;
+
+import android.car.storagemonitoring.UidIoStats;
+import android.car.storagemonitoring.UidIoStatsRecord;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.SparseArray;
+import com.android.car.procfsinspector.ProcessInfo;
+import com.android.car.systeminterface.SystemStateInterface;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import junit.framework.TestCase;
+
+/**
+ * Tests IoStatsTracker functionality.
+ */
+@MediumTest
+public class IoStatsTrackerTest extends TestCase {
+ private static final int SAMPLE_WINDOW_MS = 1000;
+ private static final List<UidIoStats> EMPTY = Collections.emptyList();
+ private static final String TAG = IoStatsTrackerTest.class.getSimpleName();
+
+ public void testNewUsersAppear() throws Exception {
+ final MockSystemStateInterface mockSystemStateInterface = new MockSystemStateInterface();
+ IoStatsTracker ioStatsTracker = new IoStatsTracker(EMPTY,
+ SAMPLE_WINDOW_MS, mockSystemStateInterface);
+
+ assertEquals(0, ioStatsTracker.getCurrentSample().size());
+ assertEquals(0, ioStatsTracker.getTotal().size());
+
+ UserActivity user0 = new UserActivity(0);
+ user0.foreground_rchar = 50;
+ user0.background_wchar = 10;
+
+ UserActivity user1 = new UserActivity(1);
+ user1.foreground_rchar = 30;
+ user1.background_wchar = 50;
+
+ UidIoStatsRecord process0 = user0.updateSystemState(mockSystemStateInterface);
+ UidIoStatsRecord process1 = user1.updateSystemState(mockSystemStateInterface);
+
+ ioStatsTracker.update(mockSystemStateInterface.mIoRecords);
+
+ assertEquals(2, ioStatsTracker.getCurrentSample().size());
+ assertEquals(2, ioStatsTracker.getTotal().size());
+
+ assertTrue(ioStatsTracker.getCurrentSample().get(0).representsSameMetrics(process0));
+ assertTrue(ioStatsTracker.getCurrentSample().get(1).representsSameMetrics(process1));
+
+ assertTrue(ioStatsTracker.getTotal().get(0).representsSameMetrics(process0));
+ assertTrue(ioStatsTracker.getTotal().get(1).representsSameMetrics(process1));
+ }
+
+ public void testUserMetricsChange() throws Exception {
+ final MockSystemStateInterface mockSystemStateInterface = new MockSystemStateInterface();
+ IoStatsTracker ioStatsTracker = new IoStatsTracker(EMPTY,
+ SAMPLE_WINDOW_MS, mockSystemStateInterface);
+
+ UserActivity user0 = new UserActivity(0);
+ user0.foreground_rchar = 50;
+ user0.background_wchar = 10;
+
+ user0.updateSystemState(mockSystemStateInterface);
+ ioStatsTracker.update(mockSystemStateInterface.mIoRecords);
+
+ user0.foreground_rchar = 60;
+ user0.foreground_wchar = 10;
+ UidIoStatsRecord process0 = user0.updateSystemState(mockSystemStateInterface);
+ ioStatsTracker.update(mockSystemStateInterface.mIoRecords);
+
+ assertEquals(1, ioStatsTracker.getCurrentSample().size());
+ assertEquals(1, ioStatsTracker.getTotal().size());
+
+ UidIoStats sample0 = ioStatsTracker.getCurrentSample().get(0);
+ UidIoStats total0 = ioStatsTracker.getTotal().get(0);
+
+ assertNotNull(sample0);
+ assertNotNull(total0);
+
+ assertTrue(total0.representsSameMetrics(process0));
+
+ assertEquals(10, sample0.foreground.bytesRead);
+ assertEquals(10, sample0.foreground.bytesWritten);
+ assertEquals(0, sample0.background.bytesWritten);
+ }
+
+ public void testUpdateNoIoProcessActive() throws Exception {
+ final MockSystemStateInterface mockSystemStateInterface = new MockSystemStateInterface();
+ IoStatsTracker ioStatsTracker = new IoStatsTracker(EMPTY,
+ SAMPLE_WINDOW_MS, mockSystemStateInterface);
+
+ UserActivity user0 = new UserActivity(0);
+ user0.foreground_rchar = 50;
+ user0.background_wchar = 10;
+
+ user0.updateSystemState(mockSystemStateInterface);
+ ioStatsTracker.update(mockSystemStateInterface.mIoRecords);
+
+ user0.spawnProcess();
+ user0.updateSystemState(mockSystemStateInterface);
+ ioStatsTracker.update(mockSystemStateInterface.mIoRecords);
+
+ assertEquals(1, ioStatsTracker.getCurrentSample().size());
+ assertEquals(1, ioStatsTracker.getTotal().size());
+
+ UidIoStats sample0 = ioStatsTracker.getCurrentSample().get(0);
+ UidIoStats total0 = ioStatsTracker.getTotal().get(0);
+
+ assertEquals(2 * SAMPLE_WINDOW_MS, sample0.runtimeMillis);
+ assertEquals(2 * SAMPLE_WINDOW_MS, total0.runtimeMillis);
+
+ assertEquals(0, sample0.foreground.bytesRead);
+ assertEquals(0, sample0.background.bytesWritten);
+ }
+
+ public void testUpdateNoIoProcessInactive() throws Exception {
+ final MockSystemStateInterface mockSystemStateInterface = new MockSystemStateInterface();
+ IoStatsTracker ioStatsTracker = new IoStatsTracker(EMPTY,
+ SAMPLE_WINDOW_MS, mockSystemStateInterface);
+
+ UserActivity user0 = new UserActivity(0);
+ user0.foreground_rchar = 50;
+ user0.background_wchar = 10;
+
+ user0.updateSystemState(mockSystemStateInterface);
+ ioStatsTracker.update(mockSystemStateInterface.mIoRecords);
+
+ user0.killProcess();
+ UidIoStatsRecord record0 = user0.updateSystemState(mockSystemStateInterface);
+ ioStatsTracker.update(mockSystemStateInterface.mIoRecords);
+
+ assertEquals(0, ioStatsTracker.getCurrentSample().size());
+ assertEquals(1, ioStatsTracker.getTotal().size());
+
+ UidIoStats total0 = ioStatsTracker.getTotal().get(0);
+ assertEquals(SAMPLE_WINDOW_MS, total0.runtimeMillis);
+ assertTrue(total0.representsSameMetrics(record0));
+ }
+
+ public void testUpdateIoHappens() throws Exception {
+ final MockSystemStateInterface mockSystemStateInterface = new MockSystemStateInterface();
+ IoStatsTracker ioStatsTracker = new IoStatsTracker(EMPTY,
+ SAMPLE_WINDOW_MS, mockSystemStateInterface);
+
+ UserActivity user0 = new UserActivity(0);
+ user0.foreground_rchar = 50;
+ user0.background_wchar = 10;
+
+ user0.updateSystemState(mockSystemStateInterface);
+ ioStatsTracker.update(mockSystemStateInterface.mIoRecords);
+
+ user0.foreground_rchar = 60;
+ UidIoStatsRecord record0 = user0.updateSystemState(mockSystemStateInterface);
+ ioStatsTracker.update(mockSystemStateInterface.mIoRecords);
+
+ assertEquals(1, ioStatsTracker.getCurrentSample().size());
+ assertEquals(1, ioStatsTracker.getTotal().size());
+
+ UidIoStats sample0 = ioStatsTracker.getCurrentSample().get(0);
+ UidIoStats total0 = ioStatsTracker.getTotal().get(0);
+
+ assertTrue(total0.representsSameMetrics(record0));
+ assertEquals(2 * SAMPLE_WINDOW_MS, total0.runtimeMillis);
+ assertEquals(2 * SAMPLE_WINDOW_MS, sample0.runtimeMillis);
+ assertEquals(10, sample0.foreground.bytesRead);
+ assertEquals(0, sample0.background.bytesWritten);
+ }
+
+ public void testUpdateGoAwayComeBackProcess() throws Exception {
+ final MockSystemStateInterface mockSystemStateInterface = new MockSystemStateInterface();
+ IoStatsTracker ioStatsTracker = new IoStatsTracker(EMPTY,
+ SAMPLE_WINDOW_MS, mockSystemStateInterface);
+
+ UserActivity user0 = new UserActivity(0);
+ user0.foreground_rchar = 50;
+ user0.background_wchar = 10;
+
+ user0.updateSystemState(mockSystemStateInterface);
+ ioStatsTracker.update(mockSystemStateInterface.mIoRecords);
+
+ ioStatsTracker.update(mockSystemStateInterface.mIoRecords);
+
+ assertEquals(0, ioStatsTracker.getCurrentSample().size());
+ assertEquals(1, ioStatsTracker.getTotal().size());
+
+ user0.spawnProcess();
+ user0.updateSystemState(mockSystemStateInterface);
+ ioStatsTracker.update(mockSystemStateInterface.mIoRecords);
+
+ assertEquals(1, ioStatsTracker.getCurrentSample().size());
+ UidIoStats sample0 = ioStatsTracker.getCurrentSample().get(0);
+ assertEquals(2 * SAMPLE_WINDOW_MS, sample0.runtimeMillis);
+ }
+
+ public void testUpdateGoAwayComeBackIo() throws Exception {
+ final MockSystemStateInterface mockSystemStateInterface = new MockSystemStateInterface();
+ IoStatsTracker ioStatsTracker = new IoStatsTracker(EMPTY,
+ SAMPLE_WINDOW_MS, mockSystemStateInterface);
+
+ UserActivity user0 = new UserActivity(0);
+ user0.foreground_rchar = 50;
+ user0.background_wchar = 10;
+
+ user0.updateSystemState(mockSystemStateInterface);
+ ioStatsTracker.update(mockSystemStateInterface.mIoRecords);
+
+ ioStatsTracker.update(mockSystemStateInterface.mIoRecords);
+
+ assertEquals(0, ioStatsTracker.getCurrentSample().size());
+ assertEquals(1, ioStatsTracker.getTotal().size());
+
+ user0.foreground_fsync = 1;
+
+ user0.updateSystemState(mockSystemStateInterface);
+ ioStatsTracker.update(mockSystemStateInterface.mIoRecords);
+
+ assertEquals(1, ioStatsTracker.getCurrentSample().size());
+
+ UidIoStats sample0 = ioStatsTracker.getCurrentSample().get(0);
+ assertEquals(2 * SAMPLE_WINDOW_MS, sample0.runtimeMillis);
+ assertEquals(1, sample0.foreground.fsyncCalls);
+ }
+
+ private static final class UserActivity {
+ private final int mUid;
+ private boolean mHasProcess;
+
+ private long foreground_rchar;
+ private long foreground_wchar;
+ private long foreground_read_bytes;
+ private long foreground_write_bytes;
+ private long foreground_fsync;
+
+ private long background_rchar;
+ private long background_wchar;
+ private long background_read_bytes;
+ private long background_write_bytes;
+ private long background_fsync;
+
+ UserActivity(int uid) {
+ mUid = uid;
+ }
+
+ void spawnProcess() {
+ mHasProcess = true;
+ }
+ void killProcess() {
+ mHasProcess = false;
+ }
+
+ UidIoStatsRecord updateSystemState(MockSystemStateInterface systemState) {
+ UidIoStatsRecord uidIoStatsRecord = new UidIoStatsRecord(mUid,
+ foreground_rchar,
+ foreground_wchar,
+ foreground_read_bytes,
+ foreground_write_bytes,
+ foreground_fsync,
+ background_rchar,
+ background_wchar,
+ background_read_bytes,
+ background_write_bytes,
+ background_fsync);
+
+ systemState.addIoRecord(uidIoStatsRecord);
+ if (mHasProcess) {
+ systemState.addProcess(new ProcessInfo(1, mUid));
+ } else {
+ systemState.removeUserProcesses(mUid);
+ }
+
+ return uidIoStatsRecord;
+ }
+ }
+
+ private final class MockSystemStateInterface implements SystemStateInterface {
+ private final List<ProcessInfo> mProcesses = new ArrayList<>();
+ private final SparseArray<UidIoStatsRecord> mIoRecords = new SparseArray<>();
+
+ @Override
+ public void shutdown() {
+ }
+
+ @Override
+ public void enterDeepSleep(int wakeupTimeSec) {
+ }
+
+ @Override
+ public void scheduleActionForBootCompleted(Runnable action, Duration delay) {
+ }
+
+ @Override
+ public boolean isWakeupCausedByTimer() {
+ return false;
+ }
+
+ @Override
+ public boolean isSystemSupportingDeepSleep() {
+ return false;
+ }
+
+ @Override
+ public synchronized List<ProcessInfo> getRunningProcesses() {
+ return mProcesses;
+ }
+
+ synchronized void addProcess(ProcessInfo processInfo) {
+ mProcesses.add(processInfo);
+ }
+
+ synchronized void removeUserProcesses(int uid) {
+ mProcesses.removeAll(
+ mProcesses.stream().filter(pi -> pi.uid == uid).collect(Collectors.toList()));
+ }
+
+ synchronized void addIoRecord(UidIoStatsRecord record) {
+ mIoRecords.put(record.uid, record);
+ }
+
+ synchronized void clear() {
+ mProcesses.clear();
+ mIoRecords.clear();
+ }
+ }
+}
diff --git a/libvehiclemonitor/java/Android.mk b/tests/common_utils/Android.mk
similarity index 78%
copy from libvehiclemonitor/java/Android.mk
copy to tests/common_utils/Android.mk
index 1ced365..19302c6 100644
--- a/libvehiclemonitor/java/Android.mk
+++ b/tests/common_utils/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2016 The Android Open Source Project
+# 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.
@@ -12,15 +12,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-#
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_MODULE := libvehiclemonitor-java
+LOCAL_MODULE := com.android.car.test.utils
+
LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/common_utils/src/com/android/car/test/utils/TemporaryDirectory.java b/tests/common_utils/src/com/android/car/test/utils/TemporaryDirectory.java
new file mode 100644
index 0000000..fa5727d
--- /dev/null
+++ b/tests/common_utils/src/com/android/car/test/utils/TemporaryDirectory.java
@@ -0,0 +1,80 @@
+/*
+ * 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.utils;
+
+import android.annotation.Nullable;
+import android.os.SystemClock;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+
+/**
+ * A utility class that creates a temporary directory.
+ * The directory, and any files contained within, are automatically deleted when calling close().
+ *
+ * Example usage:
+ *
+ * try (TemporaryDirectory tf = new TemporaryDirectory("myTest")) {
+ * ...
+ * } // directory gets deleted here
+ */
+public class TemporaryDirectory implements AutoCloseable {
+ private Path mDirectory;
+
+ private static final class DeletingVisitor extends SimpleFileVisitor<Path> {
+ FileVisitResult consume(Path path) throws IOException {
+ Files.delete(path);
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes)
+ throws IOException {
+ return consume(path);
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(Path path, IOException e)
+ throws IOException {
+ return consume(path);
+ }
+ }
+
+ private static final SimpleFileVisitor<Path> DELETE = new DeletingVisitor();
+
+ public TemporaryDirectory(@Nullable String prefix) throws IOException {
+ if (prefix == null) {
+ prefix = TemporaryDirectory.class.getSimpleName();
+ }
+ prefix += String.valueOf(SystemClock.elapsedRealtimeNanos());
+
+ mDirectory = Files.createTempDirectory(prefix);
+ }
+
+ @Override
+ public void close() throws Exception {
+ Files.walkFileTree(mDirectory, DELETE);
+ }
+
+ public File getDirectory() {
+ return mDirectory.toFile();
+ }
+}
diff --git a/tests/common_utils/src/com/android/car/test/utils/TemporaryFile.java b/tests/common_utils/src/com/android/car/test/utils/TemporaryFile.java
new file mode 100644
index 0000000..518f4a9
--- /dev/null
+++ b/tests/common_utils/src/com/android/car/test/utils/TemporaryFile.java
@@ -0,0 +1,65 @@
+/*
+ * 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.utils;
+
+import android.annotation.Nullable;
+import android.os.SystemClock;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+/**
+ * A utility class that creates a temporary file.
+ * The file is automatically deleted when calling close().
+ *
+ * Example usage:
+ *
+ * try (TemporaryFile tf = new TemporaryFile("myTest")) {
+ * ...
+ * } // file gets deleted here
+ */
+public final class TemporaryFile implements AutoCloseable {
+ private File mFile;
+
+ public TemporaryFile(@Nullable String prefix) throws IOException {
+ if (prefix == null) {
+ prefix = TemporaryFile.class.getSimpleName();
+ }
+ mFile = File.createTempFile(prefix, String.valueOf(SystemClock.elapsedRealtimeNanos()));
+ }
+
+ @Override
+ public void close() throws Exception {
+ Files.delete(mFile.toPath());
+ }
+
+ public void write(String s) throws IOException {
+ BufferedWriter writer = new BufferedWriter(new FileWriter(mFile));
+ writer.write(s);
+ writer.close();
+ }
+
+ public File getFile() {
+ return mFile;
+ }
+
+ public Path getPath() { return mFile.toPath(); }
+}
diff --git a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/E2ePerformanceTest.java b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/E2ePerformanceTest.java
index 6675713..196a6d4 100644
--- a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/E2ePerformanceTest.java
+++ b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/E2ePerformanceTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import android.annotation.Nullable;
@@ -47,6 +48,7 @@
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -259,6 +261,27 @@
final int EXPECTED_INVOCATIONS = 1000; // How many time get/set will be called.
final int EXPECTED_DURATION_MS = 2500;
+ // This is a stress test and it can be flaky because it shares resources with all currently
+ // running process. Let's have this number of attempt before giving up.
+ final int ATTEMPTS = 3;
+
+ for (int curAttempt = 0; curAttempt < ATTEMPTS; curAttempt++) {
+ long missingInvocations = stressTestHvacProperties(mgr, cfg,
+ EXPECTED_INVOCATIONS, EXPECTED_DURATION_MS);
+ if (missingInvocations == 0) return; // All done.
+
+ Log.w(TAG, "Failed to invoke get/set " + EXPECTED_INVOCATIONS
+ + " within " + EXPECTED_DURATION_MS + "ms"
+ + ", actually invoked: "
+ + (EXPECTED_INVOCATIONS - missingInvocations));
+ }
+ fail("Failed to invoke get/set " + EXPECTED_INVOCATIONS + " within "
+ + EXPECTED_DURATION_MS + "ms. Number of attempts: " + ATTEMPTS
+ + ". See logs for details.");
+ }
+
+ private long stressTestHvacProperties(CarHvacManager mgr, CarPropertyConfig<Float> cfg,
+ int EXPECTED_INVOCATIONS, int EXPECTED_DURATION_MS) throws InterruptedException {
CountDownLatch counter = new CountDownLatch(EXPECTED_INVOCATIONS);
List<Thread> threads = new ArrayList<>(Lists.newArrayList(
@@ -271,15 +294,12 @@
counter.await(EXPECTED_DURATION_MS, TimeUnit.MILLISECONDS);
long missingInvocations = counter.getCount();
- assertTrue("Failed to invoke get/set " + EXPECTED_INVOCATIONS
- + " within " + EXPECTED_DURATION_MS + "ms"
- + ", actually invoked: " + (EXPECTED_INVOCATIONS - missingInvocations),
- missingInvocations == 0);
for (Thread t : threads) {
t.join(10000); // Let thread join to not interfere with other test.
assertFalse(t.isAlive());
}
+ return missingInvocations;
}
private void invokeSetAndGetForHvacFloat(CarHvacManager mgr,
diff --git a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Utils.java b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Utils.java
index 5221907..fb97e73 100644
--- a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Utils.java
+++ b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Utils.java
@@ -81,14 +81,12 @@
return readVhalProperty(vehicle, request, f);
}
- @Nullable
static IVehicle getVehicle() throws RemoteException {
IVehicle service;
try {
- service = android.hardware.automotive.vehicle.V2_0.IVehicle.getService();
+ service = IVehicle.getService();
} catch (NoSuchElementException ex) {
- Log.d(TAG, "Couldn't connect to vehicle@2.1, connecting to vehicle@2.0...");
- service = IVehicle.getService();
+ throw new RuntimeException("Couldn't connect to vehicle@2.0", ex);
}
Log.d(TAG, "Connected to IVehicle service: " + service);
return service;
diff --git a/tools/emulator/diagnostic_injector.py b/tools/emulator/diagnostic_injector.py
index 7678576..1082e5b 100755
--- a/tools/emulator/diagnostic_injector.py
+++ b/tools/emulator/diagnostic_injector.py
@@ -25,7 +25,7 @@
import sys
import time
-import vhal_consts_2_1 as c
+import vhal_consts_2_0 as c
# vhal_emulator depends on a custom Python package that requires installation
# give user guidance should the import fail
diff --git a/tools/emulator/driving_info_generator.py b/tools/emulator/driving_info_generator.py
new file mode 100644
index 0000000..6d30dbd
--- /dev/null
+++ b/tools/emulator/driving_info_generator.py
@@ -0,0 +1,201 @@
+# 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.
+#
+
+import datetime
+import time
+
+from xml.dom import minidom
+
+import vhal_consts_2_0 as c
+
+# interval of generating driving information
+SAMPLE_INTERVAL_SECONDS = 0.5
+
+RPM_LOW = 1000
+RPM_HIGH = 3000
+
+REVERSE_DURATION_SECONDS = 10
+PARK_DURATION_SECONDS = 10
+
+# roughly 5 miles/hour
+REVERSE_SPEED_METERS_PER_SECOND = 2.3
+
+UTC_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S"
+
+
+def speed2Gear(speed):
+ """
+ Get the current gear based on speed of vehicle. The conversion may not be strictly real but
+ are close enough to a normal vehicle. Assume the vehicle is moving forward.
+ """
+ if speed < 4.4:
+ # 0 - 10 mph
+ return c.VEHICLEGEAR_GEAR_1
+ elif speed < 11.2:
+ # 10 - 25 mph
+ return c.VEHICLEGEAR_GEAR_2
+ elif speed < 20.1:
+ # 25 - 45 mph
+ return c.VEHICLEGEAR_GEAR_3
+ elif speed < 26.8:
+ # 45 - 60 mph
+ return c.VEHICLEGEAR_GEAR_4
+ else:
+ # > 60 mph
+ return c.VEHICLEGEAR_GEAR_5
+
+class GpxFrame(object):
+ """
+ A class representing a track point from GPX file
+ """
+ def __init__(self, trkptDom):
+ timeElements = trkptDom.getElementsByTagName('time')
+ if timeElements:
+ # time value in GPX is in UTC format: YYYY-MM-DDTHH:MM:SS, need to parse it
+ self.datetime = datetime.datetime.strptime(timeElements[0].firstChild.nodeValue,
+ UTC_TIME_FORMAT)
+ speedElements = trkptDom.getElementsByTagName('speed')
+ if speedElements:
+ self.speedInMps = float(speedElements[0].firstChild.nodeValue)
+
+class DrivingInfoGenerator(object):
+ """
+ A class that generates driving information like speed, odometer, rpm, etc. It is taking a
+ GPX file which describes a real route that consists of a sequence of location data. And then
+ derive driving information from those location data.
+
+ Assume it is a car with automatic transmission, so that current gear does not
+ necessarily match selected gear.
+ """
+
+ def __init__(self, gpxFile):
+ self.gpxDom = minidom.parse(gpxFile)
+ # Speed of vehicle (meter / second)
+ self.speedInMps = 0
+ # Fixed RPM with average value during driving
+ self.rpm = RPM_LOW
+ # Odometer (kilometer)
+ self.odometerInKm = 0
+ # Gear selection
+ self.selectedGear = c.VEHICLEGEAR_GEAR_PARK
+ # Current gear
+ self.currentGear = c.VEHICLEGEAR_GEAR_PARK
+ # Timestamp while driving on route defined in GPX file
+ self.datetime = 0
+
+ def _generateFrame(self, listener):
+ """
+ Handle newly generated vehicle property with listener
+ """
+ listener.handle(c.VEHICLEPROPERTY_PERF_VEHICLE_SPEED, 0, self.speedInMps, "PERF_VEHICLE_SPEED")
+ listener.handle(c.VEHICLEPROPERTY_ENGINE_RPM, 0, self.rpm, "ENGINE_RPM")
+ listener.handle(c.VEHICLEPROPERTY_PERF_ODOMETER, 0, self.odometerInKm, "PERF_ODOMETER")
+ listener.handle(c.VEHICLEPROPERTY_CURRENT_GEAR, 0, self.currentGear, "CURRENT_GEAR")
+
+ def _generateFromGpxFrame(self, gpxFrame, listener):
+ """
+ Generate a sequence of vehicle property frames from current track point to the next one.
+ The frequency of frames are pre-defined.
+
+ Some assumptions here:
+ - Two track points are very close to each other (e.g. 1 second driving distance)
+ - It is a straight line between two track point
+ - Speed is changing linearly between two track point
+
+ Given the info:
+ timestamp1 : speed1
+ timestamp2 : speed2
+
+ Vehicle properties in each frame are derived like this:
+ - Speed is calculated based on linear model
+ - Odometer is calculated based on speed and time
+ - RPM will be set to a low value if not accelerating, otherwise set to a high value
+ - Current gear will be set according to speed
+ """
+
+ duration = (gpxFrame.datetime - self.datetime).total_seconds()
+ speedIncrement = (gpxFrame.speedInMps - self.speedInMps) / duration * SAMPLE_INTERVAL_SECONDS
+ self.rpm = RPM_HIGH if speedIncrement > 0 else RPM_LOW
+
+ timeElapsed = 0
+ while timeElapsed < duration:
+ self._generateFrame(listener)
+ if timeElapsed + SAMPLE_INTERVAL_SECONDS < duration:
+ self.odometerInKm += (self.speedInMps + speedIncrement / 2.0) * SAMPLE_INTERVAL_SECONDS / 1000
+ self.speedInMps += speedIncrement
+ time.sleep(SAMPLE_INTERVAL_SECONDS)
+ else:
+ timeLeft = duration - timeElapsed
+ self.odometerInKm += (self.speedInMps + gpxFrame.speedInMps) / 2.0 * timeLeft / 1000
+ self.speedInMps = gpxFrame.speedInMps
+ time.sleep(timeLeft)
+
+ self.currentGear = speed2Gear(self.speedInMps)
+ timeElapsed += SAMPLE_INTERVAL_SECONDS
+
+ self.datetime = gpxFrame.datetime
+
+ def _generateInReverseMode(self, duration, listener):
+ print "Vehicle is reversing"
+ listener.handle(c.VEHICLEPROPERTY_GEAR_SELECTION, 0, c.VEHICLEGEAR_GEAR_REVERSE,
+ "GEAR_SELECTION")
+ listener.handle(c.VEHICLEPROPERTY_CURRENT_GEAR, 0, c.VEHICLEGEAR_GEAR_REVERSE,
+ "CURRENT_GEAR")
+ self.rpm = RPM_LOW
+ self.speedInMps = REVERSE_SPEED_METERS_PER_SECOND
+ curTime = 0
+ while curTime < duration:
+ self._generateFrame(listener)
+ self.odometerInKm += self.speedInMps * SAMPLE_INTERVAL_SECONDS / 1000
+ curTime += SAMPLE_INTERVAL_SECONDS
+ time.sleep(SAMPLE_INTERVAL_SECONDS)
+ # After reverse is done, set speed to 0
+ self.speedInMps = .0
+
+ def _generateInParkMode(self, duration, listener):
+ print "Vehicle is parked"
+ listener.handle(c.VEHICLEPROPERTY_GEAR_SELECTION, 0, c.VEHICLEGEAR_GEAR_PARK,
+ "GEAR_SELECTION")
+ listener.handle(c.VEHICLEPROPERTY_CURRENT_GEAR, 0, c.VEHICLEGEAR_GEAR_PARK,
+ "CURRENT_GEAR")
+ # Assume in park mode, engine is still on
+ self.rpm = RPM_LOW
+ self.speedInMps = .0
+ curTime = 0
+ while curTime < duration:
+ self._generateFrame(listener)
+ curTime += SAMPLE_INTERVAL_SECONDS
+ time.sleep(SAMPLE_INTERVAL_SECONDS)
+
+ def generate(self, listener):
+ # First, car is parked (probably in garage)
+ self._generateInParkMode(PARK_DURATION_SECONDS, listener)
+ # Second, car will reverse (out of garage)
+ self._generateInReverseMode(REVERSE_DURATION_SECONDS, listener)
+
+ trk = self.gpxDom.getElementsByTagName('trk')[0]
+ trkseg = trk.getElementsByTagName('trkseg')[0]
+ trkpts = trkseg.getElementsByTagName('trkpt')
+
+ print "Vehicle start moving forward"
+ listener.handle(c.VEHICLEPROPERTY_GEAR_SELECTION, 0, c.VEHICLEGEAR_GEAR_DRIVE,
+ "GEAR_SELECTION")
+
+ firstGpxFrame = GpxFrame(trkpts[0])
+ self.speedInMps = firstGpxFrame.speedInMps
+ self.datetime = firstGpxFrame.datetime
+
+ for i in xrange(1, len(trkpts)):
+ self._generateFromGpxFrame(GpxFrame(trkpts[i]), listener)
diff --git a/tools/emulator/gui.py b/tools/emulator/gui.py
index 594526a..3a0161d 100755
--- a/tools/emulator/gui.py
+++ b/tools/emulator/gui.py
@@ -24,7 +24,7 @@
import VehicleHalProto_pb2
from vhal_emulator import Vhal
-import vhal_consts_2_1 as c
+import vhal_consts_2_0 as c
# Define a simple thread that receives messages from a vhal object (v) and prints them
diff --git a/tools/emulator/user_action_generator.py b/tools/emulator/user_action_generator.py
new file mode 100644
index 0000000..3d08e17
--- /dev/null
+++ b/tools/emulator/user_action_generator.py
@@ -0,0 +1,219 @@
+# 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.
+#
+
+import time
+
+import vhal_consts_2_0 as c
+
+# A list of user actions as a subset of VHAL properties to simulate
+userActions = [
+ c.VEHICLEPROPERTY_HVAC_POWER_ON,
+ c.VEHICLEPROPERTY_HVAC_TEMPERATURE_SET,
+ c.VEHICLEPROPERTY_HVAC_FAN_SPEED,
+ c.VEHICLEPROPERTY_HVAC_FAN_DIRECTION,
+ c.VEHICLEPROPERTY_HVAC_AUTO_ON,
+ c.VEHICLEPROPERTY_HVAC_RECIRC_ON,
+ c.VEHICLEPROPERTY_HVAC_AC_ON,
+ c.VEHICLEPROPERTY_HVAC_DEFROSTER
+]
+
+propDesc = {
+ c.VEHICLEPROPERTY_HVAC_POWER_ON: 'HVAC_POWER_ON',
+ c.VEHICLEPROPERTY_HVAC_TEMPERATURE_SET: 'HVAC_TEMPERATURE_SET',
+ c.VEHICLEPROPERTY_HVAC_FAN_SPEED: 'HVAC_FAN_SPEED',
+ c.VEHICLEPROPERTY_HVAC_FAN_DIRECTION: 'HVAC_FAN_DIRECTION',
+ c.VEHICLEPROPERTY_HVAC_AUTO_ON: 'HVAC_AUTO_ON',
+ c.VEHICLEPROPERTY_HVAC_RECIRC_ON: 'HVAC_AUTO_RECIRC_ON',
+ c.VEHICLEPROPERTY_HVAC_AC_ON: 'HVAC_AC_ON',
+ c.VEHICLEPROPERTY_HVAC_DEFROSTER: 'HVAC_DEFROSTER'
+}
+
+SHORT_SLEEP_TIME_SEC = 2
+LONG_SLEEP_TIME_SEC = 30
+
+MIN_TEMPERATURE_C = 22
+MAX_TEMPERATURE_C = 28
+
+MIN_FAN_SPEED = 1
+MAX_FAN_SPEED = 5
+
+class ActionPropConfig(object):
+ """
+ A configuration class that parse the vhal property config message and hold the
+ configuration values
+ """
+ def __init__(self, vhalConfig):
+ self.supportedAreas = self._getSupportedAreas(vhalConfig)
+ self.areaConfigs = self._getAreaConfigs(vhalConfig, self.supportedAreas,
+ vhalConfig.value_type)
+
+ def _getSupportedAreas(self, vhalConfig):
+ supportedAreas = []
+ areas = vhalConfig.supported_areas
+ while areas != 0:
+ area = areas & (areas - 1)
+ area ^= areas
+ areas &= (areas - 1)
+ supportedAreas.append(area)
+
+ return supportedAreas
+
+ def _getAreaConfigs(self, vhalConfig, supportedAreas, valueType):
+ areaConfigs = {}
+ configs = vhalConfig.area_configs
+ if not configs:
+ return None
+
+ if len(configs) == 1:
+ for area in supportedAreas:
+ areaConfigs[area] = AreaConfig(configs[0], valueType)
+ else:
+ for config in configs:
+ areaConfigs[config.area_id] = AreaConfig(config, valueType)
+
+ return areaConfigs
+
+class AreaConfig(object):
+ """
+ A configuration class is representing an area config of a vhal property.
+ """
+ def __init__(self, vhalAreaConfig, valueType):
+ """
+ The class is initialized by parsing the vhal area config object
+ """
+ if valueType == c.VEHICLEPROPERTYTYPE_INT32:
+ self.min = vhalAreaConfig.min_int32_value
+ self.max = vhalAreaConfig.max_int32_value
+ elif valueType == c.VEHICLEPROPERTYTYPE_INT64:
+ self.min = vhalAreaConfig.min_int64_value
+ self.max = vhalAreaConfig.max_int64_value
+ elif valueType == c.VEHICLEPROPERTYTYPE_FLOAT:
+ self.min = vhalAreaConfig.min_float_value
+ self.max = vhalAreaConfig.max_float_value
+ else:
+ self.min = None
+ self.max = None
+
+class UserActionGenerator(object):
+ """
+ A class generate user action related vhal properties in a deterministic algorithm based on
+ pre-fetched vhal configuration
+ """
+ def __init__(self, vhal):
+ self.configs = self._getConfig(vhal)
+
+
+ def _getConfig(self, vhal):
+ """
+ Get vhal configuration for properties that need to be simulated
+ """
+ vhalConfig = {}
+ for actionProp in userActions:
+ vhal.getConfig(actionProp)
+ vhalConfig[actionProp] = ActionPropConfig(vhal.rxMsg().config[0])
+ return vhalConfig
+
+ def _adjustContinuousProperty(self, prop, begin, end, listener):
+ """
+ The method generate continuous property value from "begin" value to "end" value
+ (exclusive).
+ """
+ config = self.configs[prop]
+ for area in config.supportedAreas:
+ areaConfig = config.areaConfigs[area]
+ if begin < end:
+ begin = areaConfig.min if begin < areaConfig.min else begin
+ end = areaConfig.max if end > areaConfig.max else end
+ else:
+ begin = areaConfig.max if begin > areaConfig.max else begin
+ end = areaConfig.min if end < areaConfig.min else end
+
+ for value in self._range(begin, end):
+ listener.handle(prop, area, value, propDesc[prop])
+ time.sleep(0.2)
+
+ def _setProperty(self, prop, value, listener):
+ """
+ This method generates single property value (e.g. boolean or integer value)
+ """
+ config = self.configs[prop]
+ for area in config.supportedAreas:
+ listener.handle(prop, area, value, propDesc[prop])
+ time.sleep(1)
+
+ def _range(self, begin, end):
+ if begin < end:
+ i = begin
+ while i < end:
+ yield i
+ i += 1
+ else:
+ i = begin
+ while i > end:
+ yield i
+ i += -1
+
+ def generate(self, listener):
+ """
+ Main method that simulate user in-car actions such as HVAC
+ """
+ listener.handle(c.VEHICLEPROPERTY_HVAC_POWER_ON, c.VEHICLEAREAZONE_ROW_1,
+ 1, 'HVAC_POWER_ON')
+ time.sleep(SHORT_SLEEP_TIME_SEC)
+
+ while True:
+
+ self._setProperty(c.VEHICLEPROPERTY_HVAC_AC_ON, 1, listener)
+ time.sleep(SHORT_SLEEP_TIME_SEC)
+
+ self._setProperty(c.VEHICLEPROPERTY_HVAC_FAN_DIRECTION,
+ c.VEHICLEHVACFANDIRECTION_FACE, listener)
+ time.sleep(SHORT_SLEEP_TIME_SEC)
+
+ self._adjustContinuousProperty(c.VEHICLEPROPERTY_HVAC_TEMPERATURE_SET,
+ MAX_TEMPERATURE_C, MIN_TEMPERATURE_C, listener)
+ time.sleep(SHORT_SLEEP_TIME_SEC)
+
+ self._adjustContinuousProperty(c.VEHICLEPROPERTY_HVAC_FAN_SPEED,
+ MIN_FAN_SPEED, MAX_FAN_SPEED, listener)
+ time.sleep(LONG_SLEEP_TIME_SEC)
+
+ self._setProperty(c.VEHICLEPROPERTY_HVAC_AUTO_ON, 1, listener)
+ time.sleep(LONG_SLEEP_TIME_SEC)
+
+ self._setProperty(c.VEHICLEPROPERTY_HVAC_AUTO_ON, 0, listener)
+ time.sleep(SHORT_SLEEP_TIME_SEC)
+
+ self._setProperty(c.VEHICLEPROPERTY_HVAC_FAN_DIRECTION,
+ c.VEHICLEHVACFANDIRECTION_FACE_AND_FLOOR, listener)
+ time.sleep(LONG_SLEEP_TIME_SEC)
+
+ self._setProperty(c.VEHICLEPROPERTY_HVAC_DEFROSTER, 1, listener)
+ time.sleep(SHORT_SLEEP_TIME_SEC)
+
+ self._setProperty(c.VEHICLEPROPERTY_HVAC_FAN_DIRECTION,
+ c.VEHICLEHVACFANDIRECTION_DEFROST, listener)
+ time.sleep(SHORT_SLEEP_TIME_SEC)
+
+ self._adjustContinuousProperty(c.VEHICLEPROPERTY_HVAC_TEMPERATURE_SET,
+ MIN_TEMPERATURE_C, MAX_TEMPERATURE_C, listener)
+ time.sleep(SHORT_SLEEP_TIME_SEC)
+
+ self._adjustContinuousProperty(c.VEHICLEPROPERTY_HVAC_FAN_SPEED,
+ MAX_FAN_SPEED, MIN_FAN_SPEED, listener)
+ time.sleep(LONG_SLEEP_TIME_SEC)
+
+ self._setProperty(c.VEHICLEPROPERTY_HVAC_AC_ON, 0, listener)
+ time.sleep(SHORT_SLEEP_TIME_SEC)
diff --git a/tools/emulator/vhal_const_generate.py b/tools/emulator/vhal_const_generate.py
index 55bcd1b..df9713b 100755
--- a/tools/emulator/vhal_const_generate.py
+++ b/tools/emulator/vhal_const_generate.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3.4
+#!/usr/bin/env python3
#
# Copyright (C) 2017 The Android Open Source Project
#
@@ -94,23 +94,15 @@
print("Vehicle HAL was not found at %s. lunch may provide a correct environment, or files moved" % vhal_location)
sys.exit(1)
-vhal_20_file = os.path.join(vhal_location, '2.0', 'types.hal')
-vhal_21_file = os.path.join(vhal_location, '2.1', 'types.hal')
-
-print("Generating content from Vehicle HAL 2.0 (%s) and 2.1 (%s)" % (vhal_20_file, vhal_21_file))
-
-vhal_20_doc = parser.parse(vhal_20_file)
-vhal_21_doc = parser.parse(vhal_21_file)
-
-# 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']
-
def generateHal20():
- print("********************************")
- print("Generating VHal 2.0 constants...")
+ vhal_20_file = os.path.join(vhal_location, '2.0', 'types.hal')
+ if not(os.path.exists(vhal_20_file)):
+ print("Vehicle HAL was not found at %s. lunch may provide a correct environment, or files moved" % vhal_location)
+ sys.exit(1)
+
+ print("Generating constants from Vehicle HAL 2.0 (%s)" % (vhal_20_file))
+ vhal_20_doc = parser.parse(vhal_20_file)
+
vhal_20_file = open(os.path.join(script_directory, 'vhal_consts_2_0.py'), 'w')
printHeader(vhal_20_file)
@@ -131,17 +123,4 @@
print(" TYPE_FLOATS = [VEHICLEPROPERTYTYPE_FLOAT_VEC]", file=vhal_20_file)
print(" TYPE_COMPLEX = [VEHICLEPROPERTYTYPE_COMPLEX]", file=vhal_20_file)
-def generateHal21():
- print("********************************")
- print("Generating VHal 2.1 constants...")
- vhal_21_file = open(os.path.join(script_directory, 'vhal_consts_2_1.py'), 'w')
- printHeader(vhal_21_file)
- print('from vhal_consts_2_0 import *', file=vhal_21_file)
-
- for group in vhal_21_doc['enums']:
- print(group)
- printEnum(vhal_21_doc, group, vhal_21_file, lambda x : hex(x))
-
-
generateHal20()
-# generateHal21()
diff --git a/tools/emulator/vhal_consts_2_0.py b/tools/emulator/vhal_consts_2_0.py
index 59c430c..4eb3a1e 100644
--- a/tools/emulator/vhal_consts_2_0.py
+++ b/tools/emulator/vhal_consts_2_0.py
@@ -15,223 +15,9 @@
# DO NOT EDIT MANUALLY
# This file was autogenerated by vhal_const_generate.py
-# 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
-
-# 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
+VEHICLEAUDIOVOLUMESTATE_STATE_LIMIT_REACHED = 0x1
# SubscribeFlags
SUBSCRIBEFLAGS_UNDEFINED = 0x0
@@ -239,60 +25,40 @@
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
-VEHICLEAUDIOFOCUSREQUEST_REQUEST_GAIN_TRANSIENT = 0x2
-VEHICLEAUDIOFOCUSREQUEST_REQUEST_GAIN_TRANSIENT_MAY_DUCK = 0x3
-VEHICLEAUDIOFOCUSREQUEST_REQUEST_GAIN_TRANSIENT_NO_DUCK = 0x4
-VEHICLEAUDIOFOCUSREQUEST_REQUEST_RELEASE = 0x5
-
-# 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
-
-# VehiclePropertyAccess
-VEHICLEPROPERTYACCESS_NONE = 0x0
-VEHICLEPROPERTYACCESS_READ = 0x1
-VEHICLEPROPERTYACCESS_WRITE = 0x2
-VEHICLEPROPERTYACCESS_READ_WRITE = 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
+# 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
# VehicleAudioFocusState
VEHICLEAUDIOFOCUSSTATE_STATE_GAIN = 0x1
@@ -302,9 +68,26 @@
VEHICLEAUDIOFOCUSSTATE_STATE_LOSS = 0x5
VEHICLEAUDIOFOCUSSTATE_STATE_LOSS_TRANSIENT_EXLCUSIVE = 0x6
-# VehicleAudioStream
-VEHICLEAUDIOSTREAM_STREAM0 = 0x0
-VEHICLEAUDIOSTREAM_STREAM1 = 0x1
+# 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
+
+# VehicleHvacFanDirection
+VEHICLEHVACFANDIRECTION_FACE = 0x1
+VEHICLEHVACFANDIRECTION_FLOOR = 0x2
+VEHICLEHVACFANDIRECTION_FACE_AND_FLOOR = 0x3
+VEHICLEHVACFANDIRECTION_DEFROST = 0x4
+VEHICLEHVACFANDIRECTION_DEFROST_AND_FLOOR = 0x5
+
+# VehiclePropertyOperation
+VEHICLEPROPERTYOPERATION_GENERIC = 0x0
+VEHICLEPROPERTYOPERATION_SET = 0x1
+VEHICLEPROPERTYOPERATION_GET = 0x2
+VEHICLEPROPERTYOPERATION_SUBSCRIBE = 0x3
# DiagnosticFloatSensorIndex
DIAGNOSTICFLOATSENSORINDEX_CALCULATED_ENGINE_LOAD = 0x0
@@ -380,10 +163,9 @@
DIAGNOSTICFLOATSENSORINDEX_ENGINE_FUEL_RATE = 0x46
DIAGNOSTICFLOATSENSORINDEX_LAST_SYSTEM_INDEX = 0x46
-# VehicleAreaMirror
-VEHICLEAREAMIRROR_DRIVER_LEFT = 0x1
-VEHICLEAREAMIRROR_DRIVER_RIGHT = 0x2
-VEHICLEAREAMIRROR_DRIVER_CENTER = 0x4
+# VehicleAudioVolumeLimitIndex
+VEHICLEAUDIOVOLUMELIMITINDEX_STREAM = 0x0
+VEHICLEAUDIOVOLUMELIMITINDEX_MAX_VOLUME = 0x1
# VehicleAreaWindow
VEHICLEAREAWINDOW_FRONT_WINDSHIELD = 0x1
@@ -396,40 +178,97 @@
VEHICLEAREAWINDOW_ROW_3_LEFT = 0x1000
VEHICLEAREAWINDOW_ROW_3_RIGHT = 0x2000
-# VehiclePropertyOperation
-VEHICLEPROPERTYOPERATION_GENERIC = 0x0
-VEHICLEPROPERTYOPERATION_SET = 0x1
-VEHICLEPROPERTYOPERATION_GET = 0x2
-VEHICLEPROPERTYOPERATION_SUBSCRIBE = 0x3
+# 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
-# 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
+# VehicleAreaMirror
+VEHICLEAREAMIRROR_DRIVER_LEFT = 0x1
+VEHICLEAREAMIRROR_DRIVER_RIGHT = 0x2
+VEHICLEAREAMIRROR_DRIVER_CENTER = 0x4
-# StatusCode
-STATUSCODE_OK = 0x0
-STATUSCODE_TRY_AGAIN = 0x1
-STATUSCODE_INVALID_ARG = 0x2
-STATUSCODE_NOT_AVAILABLE = 0x3
-STATUSCODE_ACCESS_DENIED = 0x4
-STATUSCODE_INTERNAL_ERROR = 0x5
+# 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
+VEHICLEAUDIOCONTEXTFLAG_RINGTONE_FLAG = 0x2000
-# 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
+# VmsMessageWithLayerAndPublisherIdIntegerValuesIndex
+VMSMESSAGEWITHLAYERANDPUBLISHERIDINTEGERVALUESINDEX_PUBLISHER_ID = 0x4
+
+# VehiclePropertyAccess
+VEHICLEPROPERTYACCESS_NONE = 0x0
+VEHICLEPROPERTYACCESS_READ = 0x1
+VEHICLEPROPERTYACCESS_WRITE = 0x2
+VEHICLEPROPERTYACCESS_READ_WRITE = 0x3
+
+# VehicleApPowerStateIndex
+VEHICLEAPPOWERSTATEINDEX_STATE = 0x0
+VEHICLEAPPOWERSTATEINDEX_ADDITIONAL = 0x1
+
+# VmsBaseMessageIntegerValuesIndex
+VMSBASEMESSAGEINTEGERVALUESINDEX_MESSAGE_TYPE = 0x0
+
+# VehicleAudioStream
+VEHICLEAUDIOSTREAM_STREAM0 = 0x0
+VEHICLEAUDIOSTREAM_STREAM1 = 0x1
+
+# VehiclePropertyChangeMode
+VEHICLEPROPERTYCHANGEMODE_STATIC = 0x0
+VEHICLEPROPERTYCHANGEMODE_ON_CHANGE = 0x1
+VEHICLEPROPERTYCHANGEMODE_CONTINUOUS = 0x2
+VEHICLEPROPERTYCHANGEMODE_POLL = 0x3
+VEHICLEPROPERTYCHANGEMODE_ON_SET = 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
+
+# VehiclePropertyGroup
+VEHICLEPROPERTYGROUP_SYSTEM = 0x10000000
+VEHICLEPROPERTYGROUP_VENDOR = 0x20000000
+VEHICLEPROPERTYGROUP_MASK = 0xf0000000
+
+# 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
# VmsMessageType
VMSMESSAGETYPE_SUBSCRIBE = 0x1
@@ -445,6 +284,32 @@
VMSMESSAGETYPE_SUBSCRIPTIONS_CHANGE = 0xb
VMSMESSAGETYPE_DATA = 0xc
+# 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
+
+# Wheel
+WHEEL_UNKNOWN = 0x0
+WHEEL_LEFT_FRONT = 0x1
+WHEEL_RIGHT_FRONT = 0x2
+WHEEL_LEFT_REAR = 0x4
+WHEEL_RIGHT_REAR = 0x8
+
# VehicleProperty
VEHICLEPROPERTY_INVALID = 0x0
VEHICLEPROPERTY_INFO_VIN = 0x11100100
@@ -457,6 +322,7 @@
VEHICLEPROPERTY_ENGINE_COOLANT_TEMP = 0x11600301
VEHICLEPROPERTY_ENGINE_OIL_TEMP = 0x11600304
VEHICLEPROPERTY_ENGINE_RPM = 0x11600305
+VEHICLEPROPERTY_WHEEL_TICK = 0x11e00306
VEHICLEPROPERTY_GEAR_SELECTION = 0x11400400
VEHICLEPROPERTY_CURRENT_GEAR = 0x11400401
VEHICLEPROPERTY_PARKING_BRAKE_ON = 0x11200402
@@ -465,6 +331,8 @@
VEHICLEPROPERTY_NIGHT_MODE = 0x11200407
VEHICLEPROPERTY_TURN_SIGNAL_STATE = 0x11400408
VEHICLEPROPERTY_IGNITION_STATE = 0x11400409
+VEHICLEPROPERTY_ABS_ACTIVE = 0x1120040a
+VEHICLEPROPERTY_TRACTION_CONTROL_ACTIVE = 0x1120040b
VEHICLEPROPERTY_HVAC_FAN_SPEED = 0x12400500
VEHICLEPROPERTY_HVAC_FAN_DIRECTION = 0x12400501
VEHICLEPROPERTY_HVAC_TEMPERATURE_CURRENT = 0x12600502
@@ -481,8 +349,9 @@
VEHICLEPROPERTY_HVAC_STEERING_WHEEL_TEMP = 0x1140050d
VEHICLEPROPERTY_HVAC_TEMPERATURE_UNITS = 0x1240050e
VEHICLEPROPERTY_HVAC_ACTUAL_FAN_SPEED_RPM = 0x1240050f
-VEHICLEPROPERTY_HVAC_FAN_DIRECTION_AVAILABLE = 0x12400511
VEHICLEPROPERTY_HVAC_POWER_ON = 0x12200510
+VEHICLEPROPERTY_HVAC_FAN_DIRECTION_AVAILABLE = 0x12400511
+VEHICLEPROPERTY_HVAC_AUTO_RECIRC_ON = 0x12200512
VEHICLEPROPERTY_ENV_OUTSIDE_TEMPERATURE = 0x11600703
VEHICLEPROPERTY_ENV_CABIN_TEMPERATURE = 0x11600704
VEHICLEPROPERTY_RADIO_PRESET = 0x11410801
@@ -544,10 +413,6 @@
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
@@ -562,76 +427,25 @@
VEHICLEIGNITIONSTATE_ON = 0x4
VEHICLEIGNITIONSTATE_START = 0x5
-# VehiclePropertyGroup
-VEHICLEPROPERTYGROUP_SYSTEM = 0x10000000
-VEHICLEPROPERTYGROUP_VENDOR = 0x20000000
-VEHICLEPROPERTYGROUP_MASK = 0xf0000000
+# VehicleAudioVolumeCapabilityFlag
+VEHICLEAUDIOVOLUMECAPABILITYFLAG_PERSISTENT_STORAGE = 0x1
+VEHICLEAUDIOVOLUMECAPABILITYFLAG_MASTER_VOLUME_ONLY = 0x2
-# 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
+# VehicleHwKeyInputAction
+VEHICLEHWKEYINPUTACTION_ACTION_DOWN = 0x0
+VEHICLEHWKEYINPUTACTION_ACTION_UP = 0x1
-# VmsBaseMessageIntegerValuesIndex
-VMSBASEMESSAGEINTEGERVALUESINDEX_MESSAGE_TYPE = 0x0
+# VehicleAudioStreamFlag
+VEHICLEAUDIOSTREAMFLAG_STREAM0_FLAG = 0x1
+VEHICLEAUDIOSTREAMFLAG_STREAM1_FLAG = 0x2
+VEHICLEAUDIOSTREAMFLAG_STREAM2_FLAG = 0x4
-# 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
-VEHICLEPROPERTYCHANGEMODE_ON_CHANGE = 0x1
-VEHICLEPROPERTYCHANGEMODE_CONTINUOUS = 0x2
-VEHICLEPROPERTYCHANGEMODE_POLL = 0x3
-VEHICLEPROPERTYCHANGEMODE_ON_SET = 0x4
-
-# VehicleApPowerState
-VEHICLEAPPOWERSTATE_OFF = 0x0
-VEHICLEAPPOWERSTATE_DEEP_SLEEP = 0x1
-VEHICLEAPPOWERSTATE_ON_DISP_OFF = 0x2
-VEHICLEAPPOWERSTATE_ON_FULL = 0x3
-VEHICLEAPPOWERSTATE_SHUTDOWN_PREPARE = 0x4
-
-# VehicleInstrumentClusterType
-VEHICLEINSTRUMENTCLUSTERTYPE_NONE = 0x0
-VEHICLEINSTRUMENTCLUSTERTYPE_HAL_INTERFACE = 0x1
-VEHICLEINSTRUMENTCLUSTERTYPE_EXTERNAL_DISPLAY = 0x2
+# 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
# VehicleUnit
VEHICLEUNIT_SHOULD_NOT_USE = 0x0
@@ -650,22 +464,209 @@
VEHICLEUNIT_SECS = 0x53
VEHICLEUNIT_YEAR = 0x59
-# VehicleAudioVolumeLimitIndex
-VEHICLEAUDIOVOLUMELIMITINDEX_STREAM = 0x0
-VEHICLEAUDIOVOLUMELIMITINDEX_MAX_VOLUME = 0x1
+# VehicleAudioFocusIndex
+VEHICLEAUDIOFOCUSINDEX_FOCUS = 0x0
+VEHICLEAUDIOFOCUSINDEX_STREAMS = 0x1
+VEHICLEAUDIOFOCUSINDEX_EXTERNAL_FOCUS_STATE = 0x2
+VEHICLEAUDIOFOCUSINDEX_AUDIO_CONTEXTS = 0x3
+
+# 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
+
+# VehicleAudioVolumeIndex
+VEHICLEAUDIOVOLUMEINDEX_STREAM = 0x0
+VEHICLEAUDIOVOLUMEINDEX_VOLUME = 0x1
+VEHICLEAUDIOVOLUMEINDEX_STATE = 0x2
+
+# 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
+
+# VmsAvailabilityStateIntegerValuesIndex
+VMSAVAILABILITYSTATEINTEGERVALUESINDEX_SEQUENCE_NUMBER = 0x1
+VMSAVAILABILITYSTATEINTEGERVALUESINDEX_NUMBER_OF_ASSOCIATED_LAYERS = 0x2
+VMSAVAILABILITYSTATEINTEGERVALUESINDEX_LAYERS_START = 0x3
+
+# VmsSubscriptionsStateIntegerValuesIndex
+VMSSUBSCRIPTIONSSTATEINTEGERVALUESINDEX_SEQUENCE_NUMBER = 0x1
+VMSSUBSCRIPTIONSSTATEINTEGERVALUESINDEX_NUMBER_OF_LAYERS = 0x2
+VMSSUBSCRIPTIONSSTATEINTEGERVALUESINDEX_NUMBER_OF_ASSOCIATED_LAYERS = 0x3
+VMSSUBSCRIPTIONSSTATEINTEGERVALUESINDEX_SUBSCRIPTIONS_START = 0x4
+
+# VmsMessageWithLayerIntegerValuesIndex
+VMSMESSAGEWITHLAYERINTEGERVALUESINDEX_LAYER_TYPE = 0x1
+VMSMESSAGEWITHLAYERINTEGERVALUESINDEX_LAYER_SUBTYPE = 0x2
+VMSMESSAGEWITHLAYERINTEGERVALUESINDEX_LAYER_VERSION = 0x3
+
+# VehicleAudioFocusRequest
+VEHICLEAUDIOFOCUSREQUEST_REQUEST_GAIN = 0x1
+VEHICLEAUDIOFOCUSREQUEST_REQUEST_GAIN_TRANSIENT = 0x2
+VEHICLEAUDIOFOCUSREQUEST_REQUEST_GAIN_TRANSIENT_MAY_DUCK = 0x3
+VEHICLEAUDIOFOCUSREQUEST_REQUEST_GAIN_TRANSIENT_NO_DUCK = 0x4
+VEHICLEAUDIOFOCUSREQUEST_REQUEST_RELEASE = 0x5
+
+# VehicleArea
+VEHICLEAREA_GLOBAL = 0x1000000
+VEHICLEAREA_ZONE = 0x2000000
+VEHICLEAREA_WINDOW = 0x3000000
+VEHICLEAREA_MIRROR = 0x4000000
+VEHICLEAREA_SEAT = 0x5000000
+VEHICLEAREA_DOOR = 0x6000000
+VEHICLEAREA_MASK = 0xf000000
+
+# VehicleTurnSignal
+VEHICLETURNSIGNAL_NONE = 0x0
+VEHICLETURNSIGNAL_RIGHT = 0x1
+VEHICLETURNSIGNAL_LEFT = 0x2
+VEHICLETURNSIGNAL_EMERGENCY = 0x4
+
+# VehicleDisplay
+VEHICLEDISPLAY_MAIN = 0x0
+VEHICLEDISPLAY_INSTRUMENT_CLUSTER = 0x1
+
+# VehicleAudioRoutingPolicyIndex
+VEHICLEAUDIOROUTINGPOLICYINDEX_STREAM = 0x0
+VEHICLEAUDIOROUTINGPOLICYINDEX_CONTEXTS = 0x1
+
+# Obd2IgnitionMonitorKind
+OBD2IGNITIONMONITORKIND_SPARK = 0x0
+OBD2IGNITIONMONITORKIND_COMPRESSION = 0x1
+
+# VehicleApPowerBootupReason
+VEHICLEAPPOWERBOOTUPREASON_USER_POWER_ON = 0x0
+VEHICLEAPPOWERBOOTUPREASON_USER_UNLOCK = 0x1
+VEHICLEAPPOWERBOOTUPREASON_TIMER = 0x2
+
+# 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
+
+# VehicleAudioExtFocusFlag
+VEHICLEAUDIOEXTFOCUSFLAG_NONE_FLAG = 0x0
+VEHICLEAUDIOEXTFOCUSFLAG_PERMANENT_FLAG = 0x1
+VEHICLEAUDIOEXTFOCUSFLAG_TRANSIENT_FLAG = 0x2
+VEHICLEAUDIOEXTFOCUSFLAG_PLAY_ONLY_FLAG = 0x4
+VEHICLEAUDIOEXTFOCUSFLAG_MUTE_MEDIA_FLAG = 0x8
# VehicleApPowerStateShutdownParam
VEHICLEAPPOWERSTATESHUTDOWNPARAM_SHUTDOWN_IMMEDIATELY = 0x1
VEHICLEAPPOWERSTATESHUTDOWNPARAM_CAN_SLEEP = 0x2
VEHICLEAPPOWERSTATESHUTDOWNPARAM_SHUTDOWN_ONLY = 0x3
-# VehicleApPowerStateIndex
-VEHICLEAPPOWERSTATEINDEX_STATE = 0x0
-VEHICLEAPPOWERSTATEINDEX_ADDITIONAL = 0x1
+# VehicleInstrumentClusterType
+VEHICLEINSTRUMENTCLUSTERTYPE_NONE = 0x0
+VEHICLEINSTRUMENTCLUSTERTYPE_HAL_INTERFACE = 0x1
+VEHICLEINSTRUMENTCLUSTERTYPE_EXTERNAL_DISPLAY = 0x2
-# VehicleDisplay
-VEHICLEDISPLAY_MAIN = 0x0
-VEHICLEDISPLAY_INSTRUMENT_CLUSTER = 0x1
+# 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
+
+# VehicleApPowerStateConfigFlag
+VEHICLEAPPOWERSTATECONFIGFLAG_ENABLE_DEEP_SLEEP_FLAG = 0x1
+VEHICLEAPPOWERSTATECONFIGFLAG_CONFIG_SUPPORT_TIMER_POWER_ON_FLAG = 0x2
+
+# VmsOfferingMessageIntegerValuesIndex
+VMSOFFERINGMESSAGEINTEGERVALUESINDEX_PUBLISHER_ID = 0x1
+VMSOFFERINGMESSAGEINTEGERVALUESINDEX_NUMBER_OF_OFFERS = 0x2
+VMSOFFERINGMESSAGEINTEGERVALUESINDEX_OFFERING_START = 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
+
+# VehicleApPowerState
+VEHICLEAPPOWERSTATE_OFF = 0x0
+VEHICLEAPPOWERSTATE_DEEP_SLEEP = 0x1
+VEHICLEAPPOWERSTATE_ON_DISP_OFF = 0x2
+VEHICLEAPPOWERSTATE_ON_FULL = 0x3
+VEHICLEAPPOWERSTATE_SHUTDOWN_PREPARE = 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
+
+# Obd2SecondaryAirStatus
+OBD2SECONDARYAIRSTATUS_UPSTREAM = 0x1
+OBD2SECONDARYAIRSTATUS_DOWNSTREAM_OF_CATALYCIC_CONVERTER = 0x2
+OBD2SECONDARYAIRSTATUS_FROM_OUTSIDE_OR_OFF = 0x4
+OBD2SECONDARYAIRSTATUS_PUMP_ON_FOR_DIAGNOSTICS = 0x8
+
+# VehicleRadioConstants
+VEHICLERADIOCONSTANTS_VEHICLE_RADIO_PRESET_MIN_VALUE = 0x1
+
+# VehicleAudioHwVariantConfigFlag
+VEHICLEAUDIOHWVARIANTCONFIGFLAG_INTERNAL_RADIO_FLAG = 0x1
+
+# StatusCode
+STATUSCODE_OK = 0x0
+STATUSCODE_TRY_AGAIN = 0x1
+STATUSCODE_INVALID_ARG = 0x2
+STATUSCODE_NOT_AVAILABLE = 0x3
+STATUSCODE_ACCESS_DENIED = 0x4
+STATUSCODE_INTERNAL_ERROR = 0x5
# 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
deleted file mode 100644
index e5dbcff..0000000
--- a/tools/emulator/vhal_consts_2_1.py
+++ /dev/null
@@ -1,17 +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.
-#
-# DO NOT EDIT MANUALLY
-# This file was autogenerated by vhal_const_generate.py
-from vhal_consts_2_0 import *
diff --git a/tools/emulator/vhal_emulator_test.py b/tools/emulator/vhal_emulator_test.py
index ea4030d..3cf2e8b 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_1
+import vhal_consts_2_0
import vhal_emulator
import logging
@@ -49,12 +49,12 @@
_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
+ vhal_consts_2_0.VEHICLEPROPERTY_OBD2_LIVE_FRAME,
+ vhal_consts_2_0.VEHICLEPROPERTY_OBD2_FREEZE_FRAME,
+ vhal_consts_2_0.VEHICLEPROPERTY_OBD2_FREEZE_FRAME_INFO,
+ vhal_consts_2_0.VEHICLEPROPERTY_OBD2_FREEZE_FRAME_CLEAR,
+ vhal_consts_2_0.VEHICLEPROPERTY_VEHICLE_MAP_SERVICE,
+ vhal_consts_2_0.VEHICLEPROPERTY_WHEEL_TICK, # Need to support complex properties
0x21E00666 # FakeDataControllingProperty - an internal test property
]
@@ -70,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_1.VEHICLEPROPERTYTYPE_BOOLEAN:
+ elif valType == vhal_consts_2_0.VEHICLEPROPERTYTYPE_BOOLEAN:
testValue = origValue ^ 1
elif valType in self._types.TYPE_INT32:
try:
@@ -113,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_1.VEHICLEPROPERTYTYPE_BOOLEAN:
+ elif valType == vhal_consts_2_0.VEHICLEPROPERTYTYPE_BOOLEAN:
value = rxMsg.value[0].int32_values[0]
elif valType in self._types.TYPE_INT32:
value = rxMsg.value[0].int32_values[0]
@@ -305,5 +305,5 @@
self._configs = self._vhal.rxMsg().config
if __name__ == '__main__':
- v = VhalTest(vhal_consts_2_1.vhal_types_2_0)
+ v = VhalTest(vhal_consts_2_0.vhal_types_2_0)
v.runTests()
diff --git a/tools/emulator/vhal_prop_simulator.py b/tools/emulator/vhal_prop_simulator.py
new file mode 100755
index 0000000..4b1b1a8
--- /dev/null
+++ b/tools/emulator/vhal_prop_simulator.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env 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.
+#
+
+import argparse
+import time
+
+from threading import Thread
+
+import driving_info_generator
+import user_action_generator
+import vhal_consts_2_0 as c
+
+from vhal_emulator import Vhal
+
+DEFAULT_TIMEOUT_SEC = 60 * 60 # 1 hour
+
+class VhalPropSimulator(object):
+ """
+ A class simulates vhal properties by calling each generator in a separate thread. It is
+ itself a listener passed to each generator to handle vhal event
+ """
+
+ def __init__(self, device, gpxFile,):
+ self.vhal = Vhal(c.vhal_types_2_0, device)
+ self.gpxFile = gpxFile
+
+ def handle(self, prop, area_id, value, desc=None):
+ """
+ handle generated VHAL property by injecting through vhal emulator.
+ """
+ print "Generated property %s with value: %s" % (desc, value)
+ self.vhal.setProperty(prop, area_id, value)
+
+ def _startGeneratorThread(self, generator):
+ thread = Thread(target=generator.generate, args=(self,))
+ thread.daemon = True
+ thread.start()
+
+ def run(self, timeout):
+ self._startGeneratorThread(user_action_generator.UserActionGenerator(self.vhal))
+ self._startGeneratorThread(driving_info_generator.DrivingInfoGenerator(self.gpxFile))
+ time.sleep(float(timeout))
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description='Vhal Property Simulator')
+ parser.add_argument('-s', action='store', dest='deviceid', default=None)
+ parser.add_argument('--timeout', action='store', dest='timeout', default=DEFAULT_TIMEOUT_SEC)
+ parser.add_argument('--gpx', action='store', dest='gpxFile', default=None)
+ args = parser.parse_args()
+
+ simulator = VhalPropSimulator(device=args.deviceid, gpxFile=args.gpxFile)
+ simulator.run(args.timeout)
diff --git a/vehicle_monitor_service/Android.mk b/vehicle_monitor_service/Android.mk
deleted file mode 100644
index bd9f2ac..0000000
--- a/vehicle_monitor_service/Android.mk
+++ /dev/null
@@ -1,65 +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.
-#
-#
-LOCAL_PATH:= $(call my-dir)
-##################################
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(patsubst ./%,%, $(shell cd $(LOCAL_PATH); \
- find . -name "*.cpp" -and -not -name ".*"))
-
-LOCAL_SRC_FILES := $(filter-out main_vehiclemonitor.cpp, $(LOCAL_SRC_FILES))
-
-LOCAL_C_INCLUDES += \
- frameworks/base/include \
- packages/services/Car/libvehiclemonitor/include
-
-LOCAL_SHARED_LIBRARIES := \
- libbinder
-
-LOCAL_MODULE := libvehiclemonitorservice
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_CFLAGS += -Werror
-
-include $(BUILD_STATIC_LIBRARY)
-
-##################################
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- main_vehiclemonitor.cpp
-
-LOCAL_C_INCLUDES += \
- frameworks/base/include \
- packages/services/Car/libvehiclemonitor/include
-
-LOCAL_SHARED_LIBRARIES := \
- libbinder \
- liblog \
- libutils \
- libvehiclemonitor-native
-
-LOCAL_WHOLE_STATIC_LIBRARIES := \
- libvehiclemonitorservice
-
-LOCAL_STRIP_MODULE := keep_symbols
-
-LOCAL_MODULE := vehicle_monitor_service
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_CFLAGS += -Werror
-
-include $(BUILD_EXECUTABLE)
diff --git a/vehicle_monitor_service/ProcessMonitor.cpp b/vehicle_monitor_service/ProcessMonitor.cpp
deleted file mode 100644
index 521473f..0000000
--- a/vehicle_monitor_service/ProcessMonitor.cpp
+++ /dev/null
@@ -1,290 +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.
- */
-#define LOG_TAG "ProcessMonitor"
-
-#include <mutex>
-#include <sys/param.h>
-#include <dirent.h>
-
-#include "ProcessMonitor.h"
-
-#define DBG_VERBOSE
-#ifdef DBG_VERBOSE
-#define LOG_VERBOSE(x...) ALOGD(x)
-#else
-#define LOG_VERBOSE(x...)
-#endif
-
-#define MAX_LINE 256
-#define SELF_IO "/proc/self/io"
-#define NUM_PROC_DUMP 10
-
-namespace android {
-
-static bool procDeltaCpuCmp(const std::pair<pid_t, const std::shared_ptr<ProcInfo>> a,
- const std::pair<pid_t, const std::shared_ptr<ProcInfo>> b) {
- return a.second->delta_time > b.second->delta_time;
-}
-
-static bool procDeltaMemCmp(const std::pair<pid_t, const std::shared_ptr<ProcInfo>> a,
- const std::pair<pid_t, const std::shared_ptr<ProcInfo>> b) {
- return a.second->delta_rss > b.second->delta_rss;
-}
-
-static bool procDeltaWbytesCmp(const std::pair<pid_t, const std::shared_ptr<ProcInfo>> a,
- const std::pair<pid_t, const std::shared_ptr<ProcInfo>> b) {
- return a.second->delta_wbytes > b.second->delta_wbytes;
-}
-
-static bool procCpuCmp(const std::pair<pid_t, const std::shared_ptr<ProcInfo>> a,
- const std::pair<pid_t, const std::shared_ptr<ProcInfo>> b) {
- return (a.second->utime + a.second->utime) > (b.second->stime + b.second->stime);
-}
-
-static bool procMemCmp(const std::pair<pid_t, const std::shared_ptr<ProcInfo>> a,
- const std::pair<pid_t, const std::shared_ptr<ProcInfo>> b) {
- return a.second->rss > b.second->rss;
-}
-
-static bool procWbytesCmp(const std::pair<pid_t, const std::shared_ptr<ProcInfo>> a,
- const std::pair<pid_t, const std::shared_ptr<ProcInfo>> b) {
- return a.second->wbytes > b.second->wbytes;
-}
-
-ProcessMonitor::ProcessMonitor() {
- //TODO: read config from policy files.
- if (access(SELF_IO, F_OK) == -1) {
- mIoSupported = false;
- ALOGE("**** DISK I/O PROFILING DISABLED!!!!****\n"
- "Kernel doesn't support I/O profiling.");
- } else {
- mIoSupported = true;
- }
-}
-
-ProcessMonitor::~ProcessMonitor() {
-}
-
-void ProcessMonitor::dump(String8& msg) {
- std::shared_lock<std::shared_timed_mutex> lock(mMutex);
- msg.append("ProcessMonitor\n");
- msg.appendFormat("Processes count: %d\n", (int) mProcInfoMap.size());
- msg.append("Top CPU usage\n");
- dumpTopProcesses(msg, procCpuCmp);
- msg.append("Top CPU usage increase\n");
- dumpTopProcesses(msg, procDeltaCpuCmp);
- msg.append("Top memory usage\n");
- dumpTopProcesses(msg, procMemCmp);
- msg.append("Top memory usage increase\n");
- dumpTopProcesses(msg, procDeltaMemCmp);
- if (mIoSupported) {
- msg.append("Top disk IO \n");
- dumpTopProcesses(msg, procWbytesCmp);
- msg.append("Top disk IO increase \n");
- dumpTopProcesses(msg, procDeltaWbytesCmp);
- } else {
- msg.append("Disk IO monitoring not supported.\n");
- }
-}
-
-void ProcessMonitor::dumpTopProcesses(
- String8& msg,
- bool (*procCmpFn) (
- const std::pair<pid_t, const std::shared_ptr<ProcInfo>>,
- const std::pair<pid_t, const std::shared_ptr<ProcInfo>>)) {
-
- std::vector<std::pair<pid_t, std::shared_ptr<ProcInfo>>> topPids(NUM_PROC_DUMP);
- std::partial_sort_copy(mProcInfoMap.begin(),
- mProcInfoMap.end(),
- topPids.begin(),
- topPids.end(),
- *procCmpFn);
- for (auto it = topPids.begin(); it != topPids.end(); ++it) {
- msg.appendFormat("(%s) PID: %d: delta_time: %" PRIu64 ", delta_rss: %" PRIu64 ", "
- "delta_wbytes: %" PRIu64 ", utime: %" PRIu64" , stime: %" PRIu64 ", "
- "rss: %" PRIu64 ", wbytes: %" PRIu64 "\n",
- it->second->name.c_str(),
- it->first,
- it->second->delta_time,
- it->second->delta_rss,
- it->second->delta_wbytes,
- it->second->utime,
- it->second->stime,
- it->second->rss,
- it->second->wbytes);
- }
-
-}
-
-status_t ProcessMonitor::setAppPriority(uint32_t , uint32_t, uint32_t) {
- std::unique_lock<std::shared_timed_mutex> lock(mMutex);
- // TODO implement.
- return NO_ERROR;
-}
-
-status_t ProcessMonitor::process() {
- status_t status = updateProcessInfo();
- if (status != NO_ERROR) {
- return status;
- }
- return improveSystemHealth();
-}
-
-status_t ProcessMonitor::improveSystemHealth() {
- // TODO: implement policy enforcer. kill apps that abuse system.
- return NO_ERROR;
-}
-
-status_t ProcessMonitor::updateProcessInfo() {
- std::unique_lock<std::shared_timed_mutex> lock(mMutex);
- std::set<pid_t> oldPids;
- populateExistingPids(oldPids);
- DIR *procDir;
- procDir = opendir("/proc");
- if (!procDir) {
- ALOGE("Failed to open /proc dir");
- return PERMISSION_DENIED;
- }
- struct dirent *pidDir;
- pid_t pid;
- while ((pidDir = readdir(procDir))) {
- if (!isdigit(pidDir->d_name[0])) {
- continue;
- }
- pid = atoi(pidDir->d_name);
- updateOrAddProcess(pid);
- oldPids.erase(pid);
- }
- deleteOutdatedPids(oldPids);
- return NO_ERROR;
-}
-
-void ProcessMonitor::deleteOutdatedPids(std::set<pid_t>& pidSet) {
- for(auto it = pidSet.begin(); it != pidSet.end(); ++it) {
- LOG_VERBOSE("Process %d ended. Removing from process map", *it);
- mProcInfoMap.erase(*it);
- }
-}
-
-void ProcessMonitor::populateExistingPids(std::set<pid_t>& pidSet) {
- for(auto it = mProcInfoMap.begin(); it != mProcInfoMap.end(); ++it) {
- pidSet.insert(it->first);
- }
-}
-
-void ProcessMonitor::updateOrAddProcess(pid_t pid) {
- auto pidDataIt = mProcInfoMap.find(pid);
- std::shared_ptr<ProcInfo> pidData;
- if (pidDataIt == mProcInfoMap.end()) {
- pidData = std::make_shared<ProcInfo>();
- mProcInfoMap.insert(std::pair<pid_t, std::shared_ptr<ProcInfo>>(pid, pidData));
- } else {
- pidData = pidDataIt->second;
- }
- auto originalPidData = std::make_shared<ProcInfo>(*pidData);
- readStat(pidData, pid);
- if (mIoSupported) {
- readIo(pidData, pid);
- }
- readCmdline(pidData, pid);
- readStatus(pidData, pid);
- updateDiffs(pidData, originalPidData);
-}
-
-void ProcessMonitor::readStat(std::shared_ptr<ProcInfo> pidData, pid_t pid) {
- char filename[64];
- sprintf(filename, "/proc/%d/stat", pid);
- FILE *file;
- file = fopen(filename, "r");
- if (!file) {
- ALOGD("Failed to open file %s for reading", filename);
- return;
- }
- fscanf(file,
- "%*d %*s %*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
- "%" SCNu64
- "%" SCNu64 "%*d %*d %*d %*d %*d %*d %*d "
- "%*" SCNu64
- "%" SCNu64 "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d "
- "%*d",
- &pidData->utime,
- &pidData->stime,
- &pidData->rss);
- fclose(file);
-}
-
-void ProcessMonitor::readIo(std::shared_ptr<ProcInfo> pidData, pid_t pid) {
- char filename[64];
- sprintf(filename, "/proc/%d/io", pid);
- FILE *file;
- file = fopen(filename, "r");
- if (!file) {
- ALOGD("Failed to open file %s for reading", filename);
- return;
- }
- char buf[MAX_LINE];
- while (fgets(buf, MAX_LINE, file)) {
- sscanf(buf, "write_bytes: %" PRIu64, &pidData->wbytes);
- }
- fclose(file);
-}
-
-void ProcessMonitor::readCmdline(std::shared_ptr<ProcInfo> pidData, pid_t pid) {
- char filename[64];
- sprintf(filename, "/proc/%d/cmdline", pid);
- FILE *file;
- file = fopen(filename, "r");
- if (!file) {
- ALOGD("Failed to open file %s for reading", filename);
- return;
- }
- char buf[MAXPATHLEN];
- fgets(buf, MAXPATHLEN, file);
- fclose(file);
- if (strlen(buf) > 0) {
- pidData->name.assign(buf);
- }
-}
-
-void ProcessMonitor::readStatus(std::shared_ptr<ProcInfo> pidData, pid_t pid) {
- char filename[64];
- sprintf(filename, "/proc/%d/status", pid);
- FILE *file;
- file = fopen(filename, "r");
- if (!file) {
- ALOGD("Failed to open file %s for reading", filename);
- return;
- }
- char line[MAX_LINE];
- unsigned int uid;
- while (fgets(line, MAX_LINE, file)) {
- sscanf(line, "Uid: %u", &uid);
- }
- fclose(file);
- pidData->uid = uid;
-
-}
-
-void ProcessMonitor::updateDiffs(std::shared_ptr<ProcInfo> pidData,
- std::shared_ptr<ProcInfo> oldPidData) {
- pidData->delta_utime = pidData->utime - oldPidData->utime;
- pidData->delta_stime = pidData->stime - oldPidData->stime;
- pidData->delta_time = pidData->delta_utime + pidData->delta_stime;
- pidData->delta_rss = pidData->rss - oldPidData->rss;
- pidData->delta_wbytes = pidData->wbytes - oldPidData->wbytes;
-}
-
-}; // namespace android
diff --git a/vehicle_monitor_service/ProcessMonitor.h b/vehicle_monitor_service/ProcessMonitor.h
deleted file mode 100644
index d9f4d25..0000000
--- a/vehicle_monitor_service/ProcessMonitor.h
+++ /dev/null
@@ -1,94 +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.
- */
-#ifndef CAR_PROCESS_MONITOR_H_
-#define CAR_PROCESS_MONITOR_H_
-
-#include <map>
-#include <memory>
-#include <set>
-#include <string>
-#include <shared_mutex>
-
-#include <inttypes.h>
-#include <cutils/compiler.h>
-
-#include <binder/BinderService.h>
-#include <binder/IBinder.h>
-#include <utils/String8.h>
-
-#include <IVehicleMonitor.h>
-#include <HandlerThread.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-struct ProcInfo {
- pid_t pid;
- uid_t uid;
- std::string name;
- uint64_t utime;
- uint64_t stime;
- uint64_t rss;
- uint64_t wbytes;
- int64_t delta_utime;
- int64_t delta_stime;
- int64_t delta_time;
- int64_t delta_rss;
- int64_t delta_wbytes;
-};
-
-// ----------------------------------------------------------------------------
-
-// This class is used to collect information about running processes.
-// It also enforces certain policies to running apps - ex. kills non-system,
-// non-foreground applications that use too much memory, CPU, or write too much
-// data to disk.
-class ProcessMonitor {
-public:
- ProcessMonitor();
- ~ProcessMonitor();
-
- void dump(String8& msg);
- status_t setAppPriority(uint32_t pid, uint32_t uid, uint32_t priority);
- status_t process();
-
-private:
- status_t updateProcessInfo();
- void populateExistingPids(std::set<pid_t>& pidSet);
- void deleteOutdatedPids(std::set<pid_t>& pidSet);
- void updateOrAddProcess(pid_t pid);
- void readStat(std::shared_ptr<ProcInfo> pidData, pid_t pid);
- void readIo(std::shared_ptr<ProcInfo> pidData, pid_t pid);
- void readCmdline(std::shared_ptr<ProcInfo> pidData, pid_t pid);
- void readStatus(std::shared_ptr<ProcInfo> pidData, pid_t pid);
- void updateDiffs(std::shared_ptr<ProcInfo> pidData,
- std::shared_ptr<ProcInfo> oldPidData);
- status_t improveSystemHealth();
- void dumpTopProcesses(String8& msg,
- bool (*procCmpFn) (
- const std::pair<pid_t, const std::shared_ptr<ProcInfo>>,
- const std::pair<pid_t, const std::shared_ptr<ProcInfo>>));
-
-
-private:
- std::map<pid_t, std::shared_ptr<ProcInfo>> mProcInfoMap;
- bool mIoSupported;
- mutable std::shared_timed_mutex mMutex;
-};
-
-}
-
-#endif /* CAR_PROCESS_MONITOR_H_ */
diff --git a/vehicle_monitor_service/VehicleMonitorService.cpp b/vehicle_monitor_service/VehicleMonitorService.cpp
deleted file mode 100644
index 7368064..0000000
--- a/vehicle_monitor_service/VehicleMonitorService.cpp
+++ /dev/null
@@ -1,142 +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.
- */
-#define LOG_TAG "VehicleMonitor"
-
-#include <assert.h>
-
-#include <binder/PermissionCache.h>
-#include <utils/Errors.h>
-#include <utils/SystemClock.h>
-
-#include "VehicleMonitorService.h"
-
-//#define DBG_VERBOSE
-#ifdef DBG_VERBOSE
-#define LOG_VERBOSE(x...) ALOGD(x)
-#else
-#define LOG_VERBOSE(x...)
-#endif
-
-namespace android {
-
-const nsecs_t monitorInterval = 15000000000; // 15s
-
-VehicleMonitorMessageHandler::VehicleMonitorMessageHandler(const sp<Looper>& looper,
- VehicleMonitorService& service)
- : mLooper(looper),
- mService(service),
- mLastDispatchTime(0) {
- mLooper->sendMessageDelayed(monitorInterval, this, Message(COLLECT_DATA));
-}
-
-VehicleMonitorMessageHandler::~VehicleMonitorMessageHandler() {
-}
-
-void VehicleMonitorMessageHandler::dump(String8& msg) {
- msg.appendFormat("mLastDispatchTime:%" PRId64 "\n", mLastDispatchTime);
- mProcessMonitor.dump(msg);
-}
-
-void VehicleMonitorMessageHandler::doHandleCollectData() {
- {
- std::lock_guard<std::mutex> autoLock(mLock);
- mLastDispatchTime = elapsedRealtime();
- mProcessMonitor.process();
- }
-
- // TODO: do better timing for sendMessage
- mLooper->sendMessageDelayed(monitorInterval, this, Message(COLLECT_DATA));
-}
-
-void VehicleMonitorMessageHandler::handleMessage(const Message& message) {
- switch (message.what) {
- case COLLECT_DATA:
- doHandleCollectData();
- break;
- default:
- // TODO?
- break;
- }
-}
-
-// ----------------------------------------------------
-VehicleMonitorService* VehicleMonitorService::sInstance = NULL;
-
-status_t VehicleMonitorService::dump(int fd, const Vector<String16>& /*args*/) {
- static const String16 sDump("android.permission.DUMP");
- String8 msg;
- if (!PermissionCache::checkCallingPermission(sDump)) {
- msg.appendFormat("Permission Denial: "
- "can't dump VMS from pid=%d, uid=%d\n",
- IPCThreadState::self()->getCallingPid(),
- IPCThreadState::self()->getCallingUid());
- write(fd, msg.string(), msg.size());
- return NO_ERROR;
- }
- msg.appendFormat("*Handler, now in ms:%" PRId64 "\n", elapsedRealtime());
- mHandler->dump(msg);
- write(fd, msg.string(), msg.size());
- return NO_ERROR;
-}
-
-VehicleMonitorService::VehicleMonitorService() {
- sInstance = this;
-}
-
-VehicleMonitorService::~VehicleMonitorService() {
- sInstance = NULL;
-}
-
-void VehicleMonitorService::binderDied(const wp<IBinder>& who) {
- std::lock_guard<std::mutex> autoLock(mLock);
- sp<IBinder> ibinder = who.promote();
- ibinder->unlinkToDeath(this);
- // TODO: reset all priorities set by CarService.
-}
-
-void VehicleMonitorService::release() {
- std::lock_guard<std::mutex> autoLock(mLock);
- mHandlerThread->quit();
-}
-
-void VehicleMonitorService::onFirstRef() {
- std::lock_guard<std::mutex> autoLock(mLock);
- mHandlerThread = new HandlerThread();
- status_t r = mHandlerThread->start("VMS.NATIVE_LOOP");
- if (r != NO_ERROR) {
- ALOGE("cannot start handler thread, error:%d", r);
- return;
- }
- sp<VehicleMonitorMessageHandler> handler(
- new VehicleMonitorMessageHandler(mHandlerThread->getLooper(), *this));
- assert(handler.get() != NULL);
- mHandler = handler;
-}
-
-status_t VehicleMonitorService::setAppPriority(uint32_t, uint32_t, vehicle_app_priority) {
- //TODO
- return NO_ERROR;
-}
-
-status_t VehicleMonitorService::setMonitorListener(
- const sp<IVehicleMonitorListener> &listener) {
- sp<IBinder> ibinder = IInterface::asBinder(listener);
- LOG_VERBOSE("setMonitorListener, binder 0x%x", ibinder.get());
- //TODO
- return NO_ERROR;
-}
-
-}; // namespace android
diff --git a/vehicle_monitor_service/VehicleMonitorService.h b/vehicle_monitor_service/VehicleMonitorService.h
deleted file mode 100644
index 57ade9d..0000000
--- a/vehicle_monitor_service/VehicleMonitorService.h
+++ /dev/null
@@ -1,99 +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.
- */
-#ifndef CAR_VEHICLE_MONITOR_SERVICE_H_
-#define CAR_VEHICLE_MONITOR_SERVICE_H_
-
-#include <mutex>
-#include <inttypes.h>
-#include <cutils/compiler.h>
-
-#include <binder/BinderService.h>
-#include <binder/IBinder.h>
-#include <utils/String8.h>
-
-#include <ProcessMonitor.h>
-#include <IVehicleMonitor.h>
-#include <IVehicleMonitorListener.h>
-#include <HandlerThread.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-class VehicleMonitorService;
-
-/**
- * MessageHandler to handle periodic data processing.
- * Init / release is handled in the handler thread to allow upper layer to
- * allocate resource for the thread.
- */
-class VehicleMonitorMessageHandler : public MessageHandler {
- enum {
- COLLECT_DATA = 0,
- };
-
-public:
- // not passing VMS as sp as this is held by VMS always.
- VehicleMonitorMessageHandler(
- const sp<Looper>& mLooper, VehicleMonitorService& service);
- virtual ~VehicleMonitorMessageHandler();
-
- void dump(String8& msg);
-
-private:
- void handleMessage(const Message& message);
- void doHandleCollectData();
-
-private:
- mutable std::mutex mLock;
- const sp<Looper> mLooper;
- ProcessMonitor mProcessMonitor;
- VehicleMonitorService& mService;
- int64_t mLastDispatchTime;
-};
-
-// ----------------------------------------------------------------------------
-class VehicleMonitorService :
- public BinderService<VehicleMonitorService>,
- public BnVehicleMonitor,
- public IBinder::DeathRecipient {
-public:
- static const char* getServiceName() ANDROID_API {
- return IVehicleMonitor::SERVICE_NAME;
- };
-
- VehicleMonitorService();
- ~VehicleMonitorService();
- virtual status_t dump(int fd, const Vector<String16>& args);
- void release();
- virtual void binderDied(const wp<IBinder>& who);
- virtual status_t setAppPriority(
- uint32_t pid, uint32_t uid, vehicle_app_priority priority);
- virtual status_t setMonitorListener(
- const sp<IVehicleMonitorListener> &listener);
-
-private:
- // RefBase
- virtual void onFirstRef();
-private:
- static VehicleMonitorService* sInstance;
- sp<HandlerThread> mHandlerThread;
- sp<VehicleMonitorMessageHandler> mHandler;
- mutable std::mutex mLock;
-};
-
-}
-
-#endif /* CAR_VEHICLE_MONITOR_SERVICE_H_ */
diff --git a/vehicle_monitor_service/main_vehiclemonitor.cpp b/vehicle_monitor_service/main_vehiclemonitor.cpp
deleted file mode 100644
index 2994a07..0000000
--- a/vehicle_monitor_service/main_vehiclemonitor.cpp
+++ /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.
- */
-
-#define LOG_TAG "VMS"
-
-#include <signal.h>
-
-#include <binder/IServiceManager.h>
-#include <utils/Log.h>
-
-#include "VehicleMonitorService.h"
-
-using namespace android;
-
-int main(int /*argc*/, char** /*argv*/)
-{
- ALOGI("vehicle_monitor_service: starting");
- signal(SIGPIPE, SIG_IGN);
- // this will wait for service manager if not available yet.
- sp<IServiceManager> sm = defaultServiceManager();
- VehicleMonitorService::instantiate();
- ProcessState::self()->startThreadPool();
- IPCThreadState::self()->joinThreadPool();
-}