Merge "Add unit test to test CarTrustAgentEnrollmentService" into qt-dev
diff --git a/car-lib/api/test-current.txt b/car-lib/api/test-current.txt
index d6b162e..515bd93 100644
--- a/car-lib/api/test-current.txt
+++ b/car-lib/api/test-current.txt
@@ -7,6 +7,14 @@
}
+package android.car.drivingstate {
+
+ public final class CarDrivingStateManager {
+ method public void injectDrivingState(int);
+ }
+
+}
+
package android.car.media {
public final class CarAudioManager {
diff --git a/car-lib/src/android/car/CarBugreportManager.java b/car-lib/src/android/car/CarBugreportManager.java
index 398e208..7fca8ba 100644
--- a/car-lib/src/android/car/CarBugreportManager.java
+++ b/car-lib/src/android/car/CarBugreportManager.java
@@ -139,31 +139,6 @@
}
/**
- * Request a bug report. An old style flat bugreport is generated in the background.
- * The fd is closed when bugreport is written or if an exception happens.
- * This API will be removed once the clients move the new API.
- *
- * @param fd the dump file
- * @param callback the callback for reporting dump status
- * @deprecated use requestZippedbugreport instead.
- */
- @RequiresPermission(android.Manifest.permission.DUMP)
- public void requestBugreport(@NonNull ParcelFileDescriptor fd,
- @NonNull CarBugreportManagerCallback callback) {
- try {
- Preconditions.checkNotNull(fd);
- Preconditions.checkNotNull(callback);
- CarBugreportManagerCallbackWrapper wrapper =
- new CarBugreportManagerCallbackWrapper(callback, mHandler);
- mService.requestBugreport(fd, wrapper);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } finally {
- IoUtils.closeQuietly(fd);
- }
- }
-
- /**
* Request a bug report. An zipped bugreport is generated in the background.
* The file descriptors are closed when bugreport is written or if an exception happens.
* The progress protocol is described
diff --git a/car-lib/src/android/car/ICarBugreportService.aidl b/car-lib/src/android/car/ICarBugreportService.aidl
index 709cc2b..2d713da 100644
--- a/car-lib/src/android/car/ICarBugreportService.aidl
+++ b/car-lib/src/android/car/ICarBugreportService.aidl
@@ -26,13 +26,6 @@
interface ICarBugreportService {
/**
- * Starts bugreport service to capture a bugreport.
- * This method will be removed once all the clients transition to the new API.
- * @deprecated
- */
- void requestBugreport(in ParcelFileDescriptor pfd, ICarBugreportCallback callback) = 1;
-
- /**
* Starts bugreport service to capture a zipped bugreport. The caller needs to provide
* two file descriptors. The "output" file descriptor will be used to provide the actual
* zip file and the "progress" descriptor will be used to provide the progress information.
@@ -43,6 +36,6 @@
* here</a>
*/
void requestZippedBugreport(in ParcelFileDescriptor output, in ParcelFileDescriptor progress,
- ICarBugreportCallback callback) = 2;
+ ICarBugreportCallback callback) = 1;
}
diff --git a/car-lib/src/android/car/drivingstate/CarDrivingStateManager.java b/car-lib/src/android/car/drivingstate/CarDrivingStateManager.java
index 896534a..9b0626f 100644
--- a/car-lib/src/android/car/drivingstate/CarDrivingStateManager.java
+++ b/car-lib/src/android/car/drivingstate/CarDrivingStateManager.java
@@ -19,6 +19,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.car.Car;
import android.car.CarManagerBase;
import android.content.Context;
import android.os.Handler;
@@ -26,15 +28,18 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.util.Log;
import java.lang.ref.WeakReference;
/**
* API to register and get driving state related information in a car.
+ *
* @hide
*/
@SystemApi
+@TestApi
public final class CarDrivingStateManager implements CarManagerBase {
private static final String TAG = "CarDrivingStateMgr";
private static final boolean DBG = false;
@@ -64,7 +69,10 @@
/**
* Listener Interface for clients to implement to get updated on driving state changes.
+ *
+ * @hide
*/
+ @SystemApi
public interface CarDrivingStateEventListener {
/**
* Called when the car's driving state changes.
@@ -77,7 +85,10 @@
* Register a {@link CarDrivingStateEventListener} to listen for driving state changes.
*
* @param listener {@link CarDrivingStateEventListener}
+ *
+ * @hide
*/
+ @SystemApi
public synchronized void registerListener(@NonNull CarDrivingStateEventListener listener) {
if (listener == null) {
if (VDBG) {
@@ -107,7 +118,10 @@
/**
* Unregister the registered {@link CarDrivingStateEventListener} for the given driving event
* type.
+ *
+ * @hide
*/
+ @SystemApi
public synchronized void unregisterListener() {
if (mDrvStateEventListener == null) {
if (DBG) {
@@ -128,8 +142,11 @@
* Get the current value of the car's driving state.
*
* @return {@link CarDrivingStateEvent} corresponding to the given eventType
+ *
+ * @hide
*/
@Nullable
+ @SystemApi
public CarDrivingStateEvent getCurrentCarDrivingState() {
try {
return mDrivingService.getCurrentDrivingState();
@@ -139,6 +156,27 @@
}
/**
+ * Notify registered driving state change listener about injected event.
+ *
+ * @param drivingState Value in {@link CarDrivingStateEvent.CarDrivingState}.
+ *
+ * Requires Permission:
+ * {@link Car#PERMISSION_CONTROL_APP_BLOCKING}
+ *
+ * @hide
+ */
+ @TestApi
+ public void injectDrivingState(int drivingState) {
+ CarDrivingStateEvent event = new CarDrivingStateEvent(
+ drivingState, SystemClock.elapsedRealtimeNanos());
+ try {
+ mDrivingService.injectDrivingState(event);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Class that implements the listener interface and gets called back from the
* {@link com.android.car.CarDrivingStateService} across the binder interface.
*/
diff --git a/car-lib/src/android/car/drivingstate/ICarDrivingState.aidl b/car-lib/src/android/car/drivingstate/ICarDrivingState.aidl
index 30f1542..23c5d03 100644
--- a/car-lib/src/android/car/drivingstate/ICarDrivingState.aidl
+++ b/car-lib/src/android/car/drivingstate/ICarDrivingState.aidl
@@ -31,4 +31,5 @@
void registerDrivingStateChangeListener(in ICarDrivingStateChangeListener listener) = 0;
void unregisterDrivingStateChangeListener(in ICarDrivingStateChangeListener listener) = 1;
CarDrivingStateEvent getCurrentDrivingState() = 2;
+ void injectDrivingState(in CarDrivingStateEvent event) = 3;
}
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 13673b9..6db7961 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
@@ -48,6 +48,7 @@
<!-- Allow smart unlock immediately after boot because the user shouldn't have to enter a pin
code to unlock their car head unit. -->
<bool name="config_strongAuthRequiredOnBoot">false</bool>
+ <string name="config_defaultTrustAgent" translatable="false">com.android.car/com.android.car.trust.CarBleTrustAgent</string>
<!-- Show Navigation Bar -->
<bool name="config_showNavigationBar">true</bool>
diff --git a/service/src/com/android/car/CarBugreportManagerService.java b/service/src/com/android/car/CarBugreportManagerService.java
index 26a306b..338ca43 100644
--- a/service/src/com/android/car/CarBugreportManagerService.java
+++ b/service/src/com/android/car/CarBugreportManagerService.java
@@ -37,8 +37,6 @@
import com.android.internal.annotations.GuardedBy;
-import libcore.io.IoUtils;
-
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
@@ -209,38 +207,6 @@
return true;
}
-
- /**
- * This API and all descendants will be removed once the clients transition to new method
- */
- @Override
- @RequiresPermission(android.Manifest.permission.DUMP)
- public void requestBugreport(ParcelFileDescriptor pfd, ICarBugreportCallback callback) {
- if (mHandler == null) {
- // bugreport manager service is only available if the build is not a user build.
- reportError(callback, CarBugreportManagerCallback.CAR_BUGREPORT_SERVICE_NOT_AVAILABLE);
- return;
- }
-
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
- "requestBugreport");
-
- synchronized (mLock) {
- requestBugReportLocked(pfd, callback);
- }
- }
-
- @GuardedBy("mLock")
- private void requestBugReportLocked(ParcelFileDescriptor pfd, ICarBugreportCallback callback) {
- if (mIsServiceRunning) {
- Slog.w(TAG, "Bugreport Service already running");
- reportError(callback, CarBugreportManagerCallback.CAR_BUGREPORT_IN_PROGRESS);
- return;
- }
- mIsServiceRunning = true;
- mHandler.post(() -> dumpStateToFileWrapper(pfd, callback));
- }
-
private void reportError(ICarBugreportCallback callback, int errorCode) {
try {
callback.onError(errorCode);
@@ -249,64 +215,11 @@
}
}
- private void dumpStateToFileWrapper(ParcelFileDescriptor pfd, ICarBugreportCallback callback) {
- dumpStateToFile(pfd, callback);
- synchronized (mLock) {
- mIsServiceRunning = false;
- }
- }
-
@Override
public void dump(PrintWriter writer) {
// TODO(sgurun) implement
}
- private void dumpStateToFile(ParcelFileDescriptor pfd, ICarBugreportCallback callback) {
- if (DEBUG) {
- Slog.d(TAG, "Dumpstate to file");
- }
- OutputStream out = null;
- InputStream in = null;
- LocalSocket localSocket;
-
- try {
- SystemProperties.set("ctl.start", "dumpstate");
- } catch (RuntimeException e) {
- Slog.e(TAG, "Failed to start dumpstate", e);
- reportError(callback, CarBugreportManagerCallback.CAR_BUGREPORT_DUMPSTATE_FAILED);
- return;
- }
-
- try {
- localSocket = connectSocket(DUMPSTATE_SOCKET);
- } catch (IOException e) {
- Slog.e(TAG, "Timed out connecting to dumpstate socket", e);
- reportError(callback,
- CarBugreportManagerCallback.CAR_BUGREPORT_DUMPSTATE_CONNECTION_FAILED);
- // Early out if connection to socket fails.
- return;
- }
-
- try {
- in = new DataInputStream(localSocket.getInputStream());
- out = new DataOutputStream(new ParcelFileDescriptor.AutoCloseOutputStream(pfd));
- rawCopyStream(out, in);
- } catch (IOException | RuntimeException e) {
- Slog.e(TAG, "Failed to grab dump state", e);
- reportError(callback, CarBugreportManagerCallback.CAR_BUGREPORT_DUMPSTATE_FAILED);
- return;
- } finally {
- IoUtils.closeQuietly(in);
- IoUtils.closeQuietly(out);
- }
-
- try {
- callback.onFinished();
- } catch (RemoteException e) {
- Slog.e(TAG, "onFinished() failed: " + e.getMessage());
- }
- }
-
private LocalSocket connectSocket(String socketName) throws IOException {
LocalSocket socket = new LocalSocket();
// The dumpstate socket will be created by init upon receiving the
diff --git a/service/src/com/android/car/CarDrivingStateService.java b/service/src/com/android/car/CarDrivingStateService.java
index bc0fbc1..48840b7 100644
--- a/service/src/com/android/car/CarDrivingStateService.java
+++ b/service/src/com/android/car/CarDrivingStateService.java
@@ -17,6 +17,7 @@
package com.android.car;
import android.annotation.Nullable;
+import android.car.Car;
import android.car.VehicleAreaType;
import android.car.drivingstate.CarDrivingStateEvent;
import android.car.drivingstate.CarDrivingStateEvent.CarDrivingState;
@@ -220,6 +221,15 @@
return mCurrentDrivingState;
}
+ @Override
+ public synchronized void injectDrivingState(CarDrivingStateEvent event) {
+ ICarImpl.assertPermission(mContext, Car.PERMISSION_CONTROL_APP_BLOCKING);
+
+ for (DrivingStateClient client : mDrivingStateClients) {
+ client.dispatchEventToClients(event);
+ }
+ }
+
/**
* Class that holds onto client related information - listener interface, process that hosts the
* binder object etc.
diff --git a/service/src/com/android/car/CarLocationService.java b/service/src/com/android/car/CarLocationService.java
index 43d4a46..cc57856 100644
--- a/service/src/com/android/car/CarLocationService.java
+++ b/service/src/com/android/car/CarLocationService.java
@@ -16,6 +16,8 @@
package com.android.car;
+import android.car.drivingstate.CarDrivingStateEvent;
+import android.car.drivingstate.ICarDrivingStateChangeListener;
import android.car.hardware.power.CarPowerManager;
import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
import android.car.hardware.power.CarPowerManager.CarPowerStateListenerWithCompletion;
@@ -52,8 +54,8 @@
* This service stores the last known location from {@link LocationManager} when a car is parked
* and restores the location when the car is powered on.
*/
-public class CarLocationService extends BroadcastReceiver implements
- CarServiceBase, CarPowerStateListenerWithCompletion {
+public class CarLocationService extends BroadcastReceiver implements CarServiceBase,
+ CarPowerStateListenerWithCompletion {
private static final String TAG = "CarLocationService";
private static final String FILENAME = "location_cache.json";
private static final boolean DBG = true;
@@ -73,10 +75,9 @@
private HandlerThread mHandlerThread;
private Handler mHandler;
private CarPowerManager mCarPowerManager;
+ private CarDrivingStateService mCarDrivingStateService;
- public CarLocationService(
- Context context,
- CarUserManagerHelper carUserManagerHelper) {
+ public CarLocationService(Context context, CarUserManagerHelper carUserManagerHelper) {
logd("constructed");
mContext = context;
mCarUserManagerHelper = carUserManagerHelper;
@@ -90,6 +91,16 @@
filter.addAction(LocationManager.MODE_CHANGED_ACTION);
filter.addAction(LocationManager.PROVIDERS_CHANGED_ACTION);
mContext.registerReceiver(this, filter);
+ mCarDrivingStateService = CarLocalServices.getService(CarDrivingStateService.class);
+ if (mCarDrivingStateService != null) {
+ CarDrivingStateEvent event = mCarDrivingStateService.getCurrentDrivingState();
+ if (event != null && event.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING) {
+ deleteCacheFile();
+ } else {
+ mCarDrivingStateService.registerDrivingStateChangeListener(
+ mICarDrivingStateChangeEventListener);
+ }
+ }
mCarPowerManager = CarLocalServices.createCarPowerManager(mContext);
if (mCarPowerManager != null) { // null case happens for testing.
mCarPowerManager.setListenerWithCompletion(CarLocationService.this);
@@ -102,6 +113,10 @@
if (mCarPowerManager != null) {
mCarPowerManager.clearListener();
}
+ if (mCarDrivingStateService != null) {
+ mCarDrivingStateService.unregisterDrivingStateChangeListener(
+ mICarDrivingStateChangeEventListener);
+ }
mContext.unregisterReceiver(this);
}
@@ -173,6 +188,22 @@
}
}
+ private final ICarDrivingStateChangeListener mICarDrivingStateChangeEventListener =
+ new ICarDrivingStateChangeListener.Stub() {
+ @Override
+ public void onDrivingStateChanged(CarDrivingStateEvent event) {
+ logd("onDrivingStateChanged " + event);
+ if (event != null
+ && event.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING) {
+ deleteCacheFile();
+ if (mCarDrivingStateService != null) {
+ mCarDrivingStateService.unregisterDrivingStateChangeListener(
+ mICarDrivingStateChangeEventListener);
+ }
+ }
+ }
+ };
+
/**
* Tells whether or not we should check location permissions for the sake of deleting the
* location cache file when permissions are lacking. If the system user is headless but the
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index 244ee8b..31627b9 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -150,7 +150,7 @@
mAppFocusService, mCarInputService);
mSystemStateControllerService = new SystemStateControllerService(
serviceContext, mCarAudioService, this);
- mVmsBrokerService = new VmsBrokerService();
+ mVmsBrokerService = new VmsBrokerService(mContext.getPackageManager());
mVmsClientManager = new VmsClientManager(
serviceContext, mCarUserService, mUserManagerHelper, mHal.getVmsHal());
mVmsSubscriberService = new VmsSubscriberService(
@@ -169,9 +169,9 @@
CarLocalServices.addService(CarPowerManagementService.class, mCarPowerManagementService);
CarLocalServices.addService(CarUserService.class, mCarUserService);
- CarLocalServices.addService(CarTrustedDeviceService.class,
- mCarTrustedDeviceService);
+ CarLocalServices.addService(CarTrustedDeviceService.class, mCarTrustedDeviceService);
CarLocalServices.addService(SystemInterface.class, mSystemInterface);
+ CarLocalServices.addService(CarDrivingStateService.class, mCarDrivingStateService);
// Be careful with order. Service depending on other service should be inited later.
List<CarServiceBase> allServices = new ArrayList<>();
diff --git a/service/src/com/android/car/VmsPublisherService.java b/service/src/com/android/car/VmsPublisherService.java
index 3d9a104..3384e3a 100644
--- a/service/src/com/android/car/VmsPublisherService.java
+++ b/service/src/com/android/car/VmsPublisherService.java
@@ -31,12 +31,16 @@
import com.android.car.vms.VmsBrokerService;
import com.android.car.vms.VmsClientManager;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
+
/**
* Receives HAL updates by implementing VmsHalService.VmsHalListener.
* Binds to publishers and configures them to use this service.
@@ -46,12 +50,68 @@
private static final boolean DBG = true;
private static final String TAG = "VmsPublisherService";
+ @VisibleForTesting
+ static final String PACKET_COUNT_FORMAT = "Packet count for layer %s: %d\n";
+
+ @VisibleForTesting
+ static final String PACKET_SIZE_FORMAT = "Total packet size for layer %s: %d (bytes)\n";
+
+ @VisibleForTesting
+ static final String PACKET_FAILURE_COUNT_FORMAT =
+ "Total packet failure count for layer %s from %s to %s: %d\n";
+
+ @VisibleForTesting
+ static final String PACKET_FAILURE_SIZE_FORMAT =
+ "Total packet failure size for layer %s from %s to %s: %d (bytes)\n";
+
private final Context mContext;
private final VmsClientManager mClientManager;
private final VmsBrokerService mBrokerService;
private final Map<String, PublisherProxy> mPublisherProxies = Collections.synchronizedMap(
new ArrayMap<>());
+ @GuardedBy("mPacketCounts")
+ private final Map<VmsLayer, PacketCountAndSize> mPacketCounts = new ArrayMap<>();
+ @GuardedBy("mPacketFailureCounts")
+ private final Map<PacketFailureKey, PacketCountAndSize> mPacketFailureCounts = new ArrayMap<>();
+
+ // PacketCountAndSize keeps track of the cumulative size and number of packets of a specific
+ // VmsLayer that we have seen.
+ private class PacketCountAndSize {
+ long mCount;
+ long mSize;
+ }
+
+ // PacketFailureKey is a triple of the VmsLayer, the publisher and subscriber for which a packet
+ // failed to be sent.
+ private class PacketFailureKey {
+ VmsLayer mVmsLayer;
+ String mPublisher;
+ String mSubscriber;
+
+ PacketFailureKey(VmsLayer vmsLayer, String publisher, String subscriber) {
+ mVmsLayer = vmsLayer;
+ mPublisher = publisher;
+ mSubscriber = subscriber;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof PacketFailureKey)) {
+ return false;
+ }
+
+ PacketFailureKey otherKey = (PacketFailureKey) o;
+ return Objects.equals(mVmsLayer, otherKey.mVmsLayer) && Objects.equals(mPublisher,
+ otherKey.mPublisher) && Objects.equals(mSubscriber, otherKey.mSubscriber);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mVmsLayer, mPublisher, mSubscriber);
+ }
+ }
+
public VmsPublisherService(
Context context,
VmsBrokerService brokerService,
@@ -77,6 +137,28 @@
public void dump(PrintWriter writer) {
writer.println("*" + getClass().getSimpleName() + "*");
writer.println("mPublisherProxies: " + mPublisherProxies.size());
+ synchronized (mPacketCounts) {
+ for (Map.Entry<VmsLayer, PacketCountAndSize> entry : mPacketCounts.entrySet()) {
+ VmsLayer layer = entry.getKey();
+ PacketCountAndSize countAndSize = entry.getValue();
+ writer.format(PACKET_COUNT_FORMAT, layer, countAndSize.mCount);
+ writer.format(PACKET_SIZE_FORMAT, layer, countAndSize.mSize);
+ }
+ }
+ synchronized (mPacketFailureCounts) {
+ for (Map.Entry<PacketFailureKey, PacketCountAndSize> entry :
+ mPacketFailureCounts.entrySet()) {
+ PacketFailureKey key = entry.getKey();
+ PacketCountAndSize countAndSize = entry.getValue();
+ VmsLayer layer = key.mVmsLayer;
+ String publisher = key.mPublisher;
+ String subscriber = key.mSubscriber;
+ writer.format(PACKET_FAILURE_COUNT_FORMAT, layer, publisher, subscriber,
+ countAndSize.mCount);
+ writer.format(PACKET_FAILURE_SIZE_FORMAT, layer, publisher, subscriber,
+ countAndSize.mSize);
+ }
+ }
}
@Override
@@ -143,6 +225,26 @@
mBrokerService.setPublisherLayersOffering(token, offering);
}
+ private void incrementPacketCount(VmsLayer layer, long size) {
+ synchronized (mPacketCounts) {
+ PacketCountAndSize countAndSize = mPacketCounts.computeIfAbsent(layer,
+ i -> new PacketCountAndSize());
+ countAndSize.mCount++;
+ countAndSize.mSize += size;
+ }
+ }
+
+ private void incrementPacketFailure(VmsLayer layer, String publisher, String subscriber,
+ long size) {
+ synchronized (mPacketFailureCounts) {
+ PacketFailureKey key = new PacketFailureKey(layer, publisher, subscriber);
+ PacketCountAndSize countAndSize = mPacketFailureCounts.computeIfAbsent(key,
+ i -> new PacketCountAndSize());
+ countAndSize.mCount++;
+ countAndSize.mSize += size;
+ }
+ }
+
@Override
public void publish(IBinder token, VmsLayer layer, int publisherId, byte[] payload) {
assertPermission(token);
@@ -150,16 +252,32 @@
Log.d(TAG, String.format("Publishing to %s as %d (%s)", layer, publisherId, mName));
}
+ if (layer == null) {
+ return;
+ }
+
+ int payloadLength = payload != null ? payload.length : 0;
+ incrementPacketCount(layer, payloadLength);
+
// Send the message to subscribers
Set<IVmsSubscriberClient> listeners =
mBrokerService.getSubscribersForLayerFromPublisher(layer, publisherId);
if (DBG) Log.d(TAG, String.format("Number of subscribers: %d", listeners.size()));
+
+ if (listeners.size() == 0) {
+ // An empty string for the last argument is a special value signalizing zero
+ // subscribers for the VMS_PACKET_FAILURE_REPORTED atom.
+ incrementPacketFailure(layer, mName, "", payloadLength);
+ }
+
for (IVmsSubscriberClient listener : listeners) {
try {
listener.onVmsMessageReceived(layer, payload);
} catch (RemoteException ex) {
- Log.e(TAG, String.format("Unable to publish to listener: %s", listener));
+ String subscriberName = mBrokerService.getPackageName(listener);
+ incrementPacketFailure(layer, mName, subscriberName, payloadLength);
+ Log.e(TAG, String.format("Unable to publish to listener: %s", subscriberName));
}
}
}
diff --git a/service/src/com/android/car/trust/CarBleTrustAgent.java b/service/src/com/android/car/trust/CarBleTrustAgent.java
index 772afdb..08035b8 100644
--- a/service/src/com/android/car/trust/CarBleTrustAgent.java
+++ b/service/src/com/android/car/trust/CarBleTrustAgent.java
@@ -134,9 +134,6 @@
mCarTrustAgentUnlockService.stopUnlockAdvertising();
}
- // Set the trust state to false (not trusted), so unlocking is required for current user
- // in case of user switch.
- revokeTrust();
}
@Override
diff --git a/service/src/com/android/car/trust/CarTrustAgentUnlockService.java b/service/src/com/android/car/trust/CarTrustAgentUnlockService.java
index 1151c4e..c0d90d4 100644
--- a/service/src/com/android/car/trust/CarTrustAgentUnlockService.java
+++ b/service/src/com/android/car/trust/CarTrustAgentUnlockService.java
@@ -219,6 +219,7 @@
// Also disconnect from the peer.
if (mRemoteUnlockDevice != null) {
mCarTrustAgentBleManager.disconnectRemoteDevice();
+ mRemoteUnlockDevice = null;
}
}
diff --git a/service/src/com/android/car/vms/VmsBrokerService.java b/service/src/com/android/car/vms/VmsBrokerService.java
index 216c8fd..26626ff 100644
--- a/service/src/com/android/car/vms/VmsBrokerService.java
+++ b/service/src/com/android/car/vms/VmsBrokerService.java
@@ -22,19 +22,24 @@
import android.car.vms.VmsLayersOffering;
import android.car.vms.VmsOperationRecorder;
import android.car.vms.VmsSubscriptionState;
+import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.IBinder;
+import android.os.Process;
import android.util.Log;
import com.android.car.VmsLayersAvailability;
import com.android.car.VmsPublishersInfo;
import com.android.car.VmsRouting;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.IntSupplier;
/**
* Broker service facilitating subscription handling and message passing between
@@ -44,15 +49,26 @@
private static final boolean DBG = true;
private static final String TAG = "VmsBrokerService";
+ @VisibleForTesting
+ static final String HAL_CLIENT = "HalClient";
+
+ @VisibleForTesting
+ static final String UNKNOWN_PACKAGE = "UnknownPackage";
+
private CopyOnWriteArrayList<PublisherListener> mPublisherListeners =
new CopyOnWriteArrayList<>();
private CopyOnWriteArrayList<SubscriberListener> mSubscriberListeners =
new CopyOnWriteArrayList<>();
+ private PackageManager mPackageManager;
+ private IntSupplier mGetCallingPid;
+ private IntSupplier mGetCallingUid;
private final Object mLock = new Object();
@GuardedBy("mLock")
private final VmsRouting mRouting = new VmsRouting();
@GuardedBy("mLock")
+ private final Map<IBinder, String> mBinderPackage = new HashMap<>();
+ @GuardedBy("mLock")
private final Map<IBinder, Map<Integer, VmsLayersOffering>> mOfferings = new HashMap<>();
@GuardedBy("mLock")
private final VmsLayersAvailability mAvailableLayers = new VmsLayersAvailability();
@@ -95,8 +111,17 @@
/**
* Constructs new broker service.
*/
- public VmsBrokerService() {
+ public VmsBrokerService(PackageManager packageManager) {
+ this(packageManager, Binder::getCallingPid, Binder::getCallingUid);
+ }
+
+ @VisibleForTesting
+ VmsBrokerService(PackageManager packageManager, IntSupplier getCallingPid,
+ IntSupplier getCallingUid) {
if (DBG) Log.d(TAG, "Started VmsBrokerService!");
+ mPackageManager = packageManager;
+ mGetCallingPid = getCallingPid;
+ mGetCallingUid = getCallingUid;
}
/**
@@ -143,6 +168,8 @@
public void addSubscription(IVmsSubscriberClient subscriber) {
synchronized (mLock) {
mRouting.addSubscription(subscriber);
+ // Add mapping from binder to package name of subscriber.
+ mBinderPackage.computeIfAbsent(subscriber.asBinder(), k -> getCallingPackage());
}
}
@@ -172,6 +199,9 @@
// Add the listeners subscription to the layer
mRouting.addSubscription(subscriber, layer);
+
+ // Add mapping from binder to package name of subscriber.
+ mBinderPackage.computeIfAbsent(subscriber.asBinder(), k -> getCallingPackage());
}
if (firstSubscriptionForLayer) {
notifyOfSubscriptionChange();
@@ -219,6 +249,9 @@
// Add the listeners subscription to the layer
mRouting.addSubscription(subscriber, layer, publisherId);
+
+ // Add mapping from binder to package name of subscriber.
+ mBinderPackage.computeIfAbsent(subscriber.asBinder(), k -> getCallingPackage());
}
if (firstSubscriptionForLayer) {
notifyOfSubscriptionChange();
@@ -263,6 +296,9 @@
boolean subscriptionStateChanged;
synchronized (mLock) {
subscriptionStateChanged = mRouting.removeDeadSubscriber(subscriber);
+
+ // Remove mapping from binder to package name of subscriber.
+ mBinderPackage.remove(subscriber.asBinder());
}
if (subscriptionStateChanged) {
notifyOfSubscriptionChange();
@@ -358,6 +394,15 @@
}
}
+ /**
+ * Gets the package name for a given IVmsSubscriberClient
+ */
+ public String getPackageName(IVmsSubscriberClient subscriber) {
+ synchronized (mLock) {
+ return mBinderPackage.get(subscriber.asBinder());
+ }
+ }
+
private void updateLayerAvailability() {
Set<VmsLayersOffering> allPublisherOfferings = new HashSet<>();
synchronized (mLock) {
@@ -388,4 +433,21 @@
listener.onLayersAvailabilityChange(availableLayers);
}
}
+
+ // If we're in a binder call, returns back the package name of the caller of the binder call.
+ private String getCallingPackage() {
+ int callingPid = mGetCallingPid.getAsInt();
+ // Since the HAL lives in the same process, if the callingPid is equal to this process's
+ // PID, we know it's the HAL client.
+ if (callingPid == Process.myPid()) {
+ return HAL_CLIENT;
+ }
+ int callingUid = mGetCallingUid.getAsInt();
+ String packageName = mPackageManager.getNameForUid(callingUid);
+ if (packageName == null) {
+ return UNKNOWN_PACKAGE;
+ } else {
+ return packageName;
+ }
+ }
}
diff --git a/service/src/com/android/car/vms/VmsClientManager.java b/service/src/com/android/car/vms/VmsClientManager.java
index ea86ef3..d3c39ff 100644
--- a/service/src/com/android/car/vms/VmsClientManager.java
+++ b/service/src/com/android/car/vms/VmsClientManager.java
@@ -337,6 +337,9 @@
mHalClient = null;
notifyListenersOnClientDisconnected(HAL_CLIENT_NAME);
}
+ synchronized (mRebindCounts) {
+ mRebindCounts.computeIfAbsent(HAL_CLIENT_NAME, k -> new AtomicLong()).incrementAndGet();
+ }
}
class ClientConnection implements ServiceConnection {
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarHvacManagerTest.java b/tests/android_car_api_test/src/android/car/apitest/CarHvacManagerTest.java
index 2fc57eb..7a3b7b5 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarHvacManagerTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarHvacManagerTest.java
@@ -120,13 +120,13 @@
for (int areaId : areaIds) {
assertTrue(property.hasArea(areaId));
- int min = property.getMinValue(areaId);
- int max = property.getMaxValue(areaId);
+ int min = property.getMinValue(areaId) == null ? 0 : property.getMinValue(areaId);
+ int max = property.getMaxValue(areaId) == null ? 0 : property.getMaxValue(areaId);
assertTrue(min <= max);
}
} else {
- int min = property.getMinValue();
- int max = property.getMaxValue();
+ int min = property.getMinValue() == null ? 0 : property.getMinValue();
+ int max = property.getMaxValue() == null ? 0 : property.getMinValue();
assertTrue(min <= max);
for (int i = 0; i < 32; i++) {
assertFalse(property.hasArea(0x1 << i));
@@ -143,15 +143,17 @@
assertTrue(areaIds.length > 0);
assertEquals(areaIds.length, property.getAreaCount());
- for (int areId : areaIds) {
- assertTrue(property.hasArea(areId));
- float min = property.getMinValue(areId);
- float max = property.getMaxValue(areId);
+ for (int areaId : areaIds) {
+ assertTrue(property.hasArea(areaId));
+ float min =
+ property.getMinValue(areaId) == null ? 0f : property.getMinValue(areaId);
+ float max =
+ property.getMaxValue(areaId) == null ? 0f : property.getMinValue(areaId);
assertTrue(min <= max);
}
} else {
- float min = property.getMinValue();
- float max = property.getMaxValue();
+ float min = property.getMinValue() == null ? 0f : property.getMinValue();
+ float max = property.getMaxValue() == null ? 0f : property.getMinValue();
assertTrue(min <= max);
for (int i = 0; i < 32; i++) {
assertFalse(property.hasArea(0x1 << i));
diff --git a/tests/carservice_test/src/com/android/car/vms/VmsBrokerServiceTest.java b/tests/carservice_test/src/com/android/car/vms/VmsBrokerServiceTest.java
new file mode 100644
index 0000000..5ce7df5
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/vms/VmsBrokerServiceTest.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2019 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.vms;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.car.vms.IVmsSubscriberClient;
+import android.car.vms.VmsLayer;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Process;
+
+import androidx.test.filters.MediumTest;
+
+import org.junit.Test;
+
+import java.util.function.IntSupplier;
+
+@MediumTest
+public class VmsBrokerServiceTest {
+
+ class MockIntProvider implements IntSupplier {
+ private int[] mInts;
+ private int mIdx;
+
+ MockIntProvider(int... ints) {
+ mInts = ints;
+ mIdx = 0;
+ }
+
+ public int getAsInt() {
+ int ret = mInts[mIdx];
+ mIdx++;
+ return ret;
+ }
+ }
+
+ /**
+ * Test that adding a subscriber to VmsBrokerService also keeps track of the package name for
+ * a given subscriber. Also checks that if we remove a dead subscriber, we no longer track the
+ * package name associated with it.
+ */
+ @Test
+ public void testAddSubscription() {
+ PackageManager packageManager = mock(PackageManager.class);
+ IVmsSubscriberClient subscriberClient = mock(IVmsSubscriberClient.class);
+ Binder binder = mock(Binder.class);
+ VmsLayer layer = mock(VmsLayer.class);
+ when(packageManager.getNameForUid(0)).thenReturn("test.package1");
+ when(subscriberClient.asBinder()).thenReturn(binder);
+
+ VmsBrokerService broker = new VmsBrokerService(packageManager, () -> 200, () -> 0);
+ broker.addSubscription(subscriberClient);
+ assertThat(broker.getPackageName(subscriberClient)).isEqualTo("test.package1");
+ broker.removeDeadSubscriber(subscriberClient);
+ assertThat(broker.getPackageName(subscriberClient)).isNull();
+ }
+
+ @Test
+ public void testAddSubscriptionLayer() {
+ PackageManager packageManager = mock(PackageManager.class);
+ IVmsSubscriberClient subscriberClient = mock(IVmsSubscriberClient.class);
+ Binder binder = mock(Binder.class);
+ VmsLayer layer = mock(VmsLayer.class);
+ when(packageManager.getNameForUid(0)).thenReturn("test.package2");
+ when(subscriberClient.asBinder()).thenReturn(binder);
+
+ VmsBrokerService broker = new VmsBrokerService(packageManager, () -> 200, () -> 0);
+ broker.addSubscription(subscriberClient, layer);
+ assertThat(broker.getPackageName(subscriberClient)).isEqualTo("test.package2");
+ broker.removeDeadSubscriber(subscriberClient);
+ assertThat(broker.getPackageName(subscriberClient)).isNull();
+ }
+
+ @Test
+ public void testAddSubscriptionLayerVersion() {
+ PackageManager packageManager = mock(PackageManager.class);
+ IVmsSubscriberClient subscriberClient = mock(IVmsSubscriberClient.class);
+ Binder binder = mock(Binder.class);
+ VmsLayer layer = mock(VmsLayer.class);
+ when(packageManager.getNameForUid(0)).thenReturn("test.package3");
+ when(subscriberClient.asBinder()).thenReturn(binder);
+
+ VmsBrokerService broker = new VmsBrokerService(packageManager, () -> 200, () -> 0);
+ broker.addSubscription(subscriberClient, layer, 1234);
+ assertThat(broker.getPackageName(subscriberClient)).isEqualTo("test.package3");
+ broker.removeDeadSubscriber(subscriberClient);
+ assertThat(broker.getPackageName(subscriberClient)).isNull();
+ }
+
+ @Test
+ public void testMultipleSubscriptionsSameClientCallsPackageManagerOnce() {
+ PackageManager packageManager = mock(PackageManager.class);
+ IVmsSubscriberClient subscriberClient = mock(IVmsSubscriberClient.class);
+ Binder binder = mock(Binder.class);
+ when(subscriberClient.asBinder()).thenReturn(binder);
+ when(packageManager.getNameForUid(0)).thenReturn("test.package3");
+
+ VmsBrokerService broker = new VmsBrokerService(packageManager, () -> 0, () -> 0);
+ broker.addSubscription(subscriberClient);
+ broker.addSubscription(subscriberClient);
+ // The second argument isn't necessary but is here for clarity.
+ verify(packageManager, times(1)).getNameForUid(0);
+ }
+
+ @Test
+ public void testUnknownPackageName() {
+ PackageManager packageManager = mock(PackageManager.class);
+ IVmsSubscriberClient subscriberClient = mock(IVmsSubscriberClient.class);
+ Binder binder = mock(Binder.class);
+ when(subscriberClient.asBinder()).thenReturn(binder);
+ when(packageManager.getNameForUid(0)).thenReturn(null);
+
+ VmsBrokerService broker = new VmsBrokerService(packageManager, () -> 0, () -> 0);
+ broker.addSubscription(subscriberClient);
+ assertThat(broker.getPackageName(subscriberClient)).isEqualTo(
+ VmsBrokerService.UNKNOWN_PACKAGE);
+ }
+
+ /**
+ * Tests that if the HAL is a subscriber, we record its package name as HalClient.
+ */
+ @Test
+ public void testAddingHalSubscriberSavesPackageName() {
+ PackageManager packageManager = mock(PackageManager.class);
+ IVmsSubscriberClient subscriberClient = mock(IVmsSubscriberClient.class);
+
+ VmsBrokerService broker = new VmsBrokerService(packageManager, () -> Process.myPid(),
+ () -> Process.SYSTEM_UID);
+ broker.addSubscription(subscriberClient);
+ assertThat(broker.getPackageName(subscriberClient)).isEqualTo(VmsBrokerService.HAL_CLIENT);
+ }
+
+ @Test
+ public void testMultipleSubscriptionsPackageManager() {
+ PackageManager packageManager = mock(PackageManager.class);
+
+ IVmsSubscriberClient subscriberClient1 = mock(IVmsSubscriberClient.class);
+ Binder binder1 = mock(Binder.class);
+ when(subscriberClient1.asBinder()).thenReturn(binder1);
+
+ IVmsSubscriberClient subscriberClient2 = mock(IVmsSubscriberClient.class);
+ Binder binder2 = mock(Binder.class);
+ when(subscriberClient2.asBinder()).thenReturn(binder2);
+
+ IVmsSubscriberClient subscriberClient3 = mock(IVmsSubscriberClient.class);
+ Binder binder3 = mock(Binder.class);
+ when(subscriberClient3.asBinder()).thenReturn(binder3);
+
+ // Tests a client with a different UID but the same package as subscriberClient1
+ IVmsSubscriberClient subscriberClient4 = mock(IVmsSubscriberClient.class);
+ Binder binder4 = mock(Binder.class);
+ when(subscriberClient4.asBinder()).thenReturn(binder4);
+
+ when(packageManager.getNameForUid(0)).thenReturn("test.package0");
+ when(packageManager.getNameForUid(1)).thenReturn("test.package1");
+ when(packageManager.getNameForUid(2)).thenReturn("test.package2");
+ when(packageManager.getNameForUid(3)).thenReturn("test.package0");
+
+ VmsBrokerService broker = new VmsBrokerService(packageManager, () -> 10,
+ new MockIntProvider(0, 1, 2, 3));
+
+ broker.addSubscription(subscriberClient1);
+ broker.addSubscription(subscriberClient2);
+ broker.addSubscription(subscriberClient3);
+ broker.addSubscription(subscriberClient4);
+
+ verify(packageManager).getNameForUid(0);
+ verify(packageManager).getNameForUid(1);
+ verify(packageManager).getNameForUid(2);
+ verify(packageManager).getNameForUid(3);
+
+ assertThat(broker.getPackageName(subscriberClient1)).isEqualTo("test.package0");
+ assertThat(broker.getPackageName(subscriberClient2)).isEqualTo("test.package1");
+ assertThat(broker.getPackageName(subscriberClient3)).isEqualTo("test.package2");
+ assertThat(broker.getPackageName(subscriberClient4)).isEqualTo("test.package0");
+ }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/CarLocationServiceTest.java b/tests/carservice_unit_test/src/com/android/car/CarLocationServiceTest.java
index 4290354..d08d32a 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarLocationServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarLocationServiceTest.java
@@ -28,6 +28,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.car.drivingstate.CarDrivingStateEvent;
+import android.car.drivingstate.ICarDrivingStateChangeListener;
import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
import android.car.userlib.CarUserManagerHelper;
import android.content.Context;
@@ -73,6 +75,7 @@
* 2. {@link LocationManager} provides dummy {@link Location}s.
* 3. {@link CarUserManagerHelper} tells whether or not the system user is headless.
* 4. {@link SystemInterface} tells where to store system files.
+ * 5. {@link CarDrivingStateService} tells about driving state changes.
*/
@RunWith(AndroidJUnit4.class)
public class CarLocationServiceTest {
@@ -90,6 +93,8 @@
private CarUserManagerHelper mMockCarUserManagerHelper;
@Mock
private SystemInterface mMockSystemInterface;
+ @Mock
+ private CarDrivingStateService mMockCarDrivingStateService;
/**
* Initialize all of the objects with the @Mock annotation.
@@ -111,6 +116,9 @@
};
CarLocalServices.removeServiceForTest(SystemInterface.class);
CarLocalServices.addService(SystemInterface.class, mMockSystemInterface);
+ CarLocalServices.removeServiceForTest(CarDrivingStateService.class);
+ CarLocalServices.addService(CarDrivingStateService.class, mMockCarDrivingStateService);
+ when(mMockSystemInterface.getSystemCarDir()).thenReturn(mTempDirectory);
}
@After
@@ -142,10 +150,13 @@
*/
@Test
public void testRegistersToReceiveEvents() {
- ArgumentCaptor<IntentFilter> argument = ArgumentCaptor.forClass(IntentFilter.class);
+ ArgumentCaptor<IntentFilter> intentFilterArgument = ArgumentCaptor.forClass(
+ IntentFilter.class);
mCarLocationService.init();
- verify(mMockContext).registerReceiver(eq(mCarLocationService), argument.capture());
- IntentFilter intentFilter = argument.getValue();
+ verify(mMockContext).registerReceiver(eq(mCarLocationService),
+ intentFilterArgument.capture());
+ verify(mMockCarDrivingStateService).registerDrivingStateChangeListener(any());
+ IntentFilter intentFilter = intentFilterArgument.getValue();
assertEquals(3, intentFilter.countActions());
String[] actions = {intentFilter.getAction(0), intentFilter.getAction(1),
intentFilter.getAction(2)};
@@ -159,8 +170,10 @@
*/
@Test
public void testUnregistersEventReceivers() {
+ mCarLocationService.init();
mCarLocationService.release();
verify(mMockContext).unregisterReceiver(mCarLocationService);
+ verify(mMockCarDrivingStateService).unregisterDrivingStateChangeListener(any());
}
/**
@@ -177,7 +190,6 @@
ArgumentCaptor<Location> argument = ArgumentCaptor.forClass(Location.class);
when(mMockContext.getSystemService(Context.LOCATION_SERVICE))
.thenReturn(mMockLocationManager);
- when(mMockSystemInterface.getSystemCarDir()).thenReturn(mTempDirectory);
when(mMockLocationManager.injectLocation(argument.capture())).thenReturn(true);
when(mMockCarUserManagerHelper.isHeadlessSystemUser()).thenReturn(true);
@@ -304,7 +316,6 @@
timbuktu.setAccuracy(13.75f);
timbuktu.setTime(currentTime);
timbuktu.setElapsedRealtimeNanos(elapsedTime);
- when(mMockSystemInterface.getSystemCarDir()).thenReturn(mTempDirectory);
when(mMockContext.getSystemService(Context.LOCATION_SERVICE))
.thenReturn(mMockLocationManager);
when(mMockLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER))
@@ -326,7 +337,7 @@
* Test that the {@link CarLocationService} does not throw an exception on SUSPEND_EXIT events.
*/
@Test
- public void testDoesNotThrowExceptionUponStateChanged() {
+ public void testDoesNotThrowExceptionUponPowerStateChanged() {
try {
mCarLocationService.onStateChanged(CarPowerStateListener.SUSPEND_ENTER, null);
mCarLocationService.onStateChanged(CarPowerStateListener.SUSPEND_EXIT, null);
@@ -361,12 +372,16 @@
*/
@Test
public void testDeletesCacheFileWhenLocationIsDisabled() throws Exception {
+ writeCacheFile("{\"provider\":\"latitude\":16.7666,\"longitude\": \"accuracy\":1.0}");
when(mMockContext.getSystemService(Context.LOCATION_SERVICE))
.thenReturn(mMockLocationManager);
when(mMockLocationManager.isLocationEnabled()).thenReturn(false);
mCarLocationService.init();
+ assertTrue(getLocationCacheFile().exists());
+
mCarLocationService.onReceive(mMockContext,
new Intent(LocationManager.MODE_CHANGED_ACTION));
+
verify(mMockLocationManager, times(1)).isLocationEnabled();
assertFalse(getLocationCacheFile().exists());
}
@@ -394,18 +409,49 @@
*/
@Test
public void testDeletesCacheFileWhenTheGPSProviderIsDisabled() throws Exception {
+ writeCacheFile("{\"provider\":\"latitude\":16.7666,\"longitude\": \"accuracy\":1.0}");
when(mMockContext.getSystemService(Context.LOCATION_SERVICE))
.thenReturn(mMockLocationManager);
when(mMockLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)).thenReturn(
false);
mCarLocationService.init();
+ assertTrue(getLocationCacheFile().exists());
+
mCarLocationService.onReceive(mMockContext,
new Intent(LocationManager.PROVIDERS_CHANGED_ACTION));
+
verify(mMockLocationManager, times(1))
.isProviderEnabled(LocationManager.GPS_PROVIDER);
assertFalse(getLocationCacheFile().exists());
}
+ /**
+ * Test that the {@link CarLocationService} deletes location_cache.json when the car enters a
+ * moving driving state.
+ */
+ @Test
+ public void testDeletesCacheFileWhenDrivingStateBecomesMoving() throws Exception {
+ writeCacheFile("{\"provider\":\"latitude\":16.7666,\"longitude\": \"accuracy\":1.0}");
+ when(mMockContext.getSystemService(Context.LOCATION_SERVICE))
+ .thenReturn(mMockLocationManager);
+ when(mMockLocationManager.isLocationEnabled()).thenReturn(false);
+ mCarLocationService.init();
+ ArgumentCaptor<ICarDrivingStateChangeListener> changeListenerArgument =
+ ArgumentCaptor.forClass(ICarDrivingStateChangeListener.class);
+ verify(mMockCarDrivingStateService).registerDrivingStateChangeListener(
+ changeListenerArgument.capture());
+ ICarDrivingStateChangeListener changeListener = changeListenerArgument.getValue();
+ assertTrue(getLocationCacheFile().exists());
+
+ changeListener.onDrivingStateChanged(
+ new CarDrivingStateEvent(CarDrivingStateEvent.DRIVING_STATE_MOVING,
+ SystemClock.elapsedRealtimeNanos()));
+
+ verify(mMockLocationManager, times(0)).isLocationEnabled();
+ verify(mMockCarDrivingStateService, times(1)).unregisterDrivingStateChangeListener(any());
+ assertFalse(getLocationCacheFile().exists());
+ }
+
private void writeCacheFile(String json) throws IOException {
FileOutputStream fos = new FileOutputStream(getLocationCacheFile());
fos.write(json.getBytes());
diff --git a/tests/carservice_unit_test/src/com/android/car/VmsPublisherServiceTest.java b/tests/carservice_unit_test/src/com/android/car/VmsPublisherServiceTest.java
index 6e0c2fe..2c63ef5 100644
--- a/tests/carservice_unit_test/src/com/android/car/VmsPublisherServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/VmsPublisherServiceTest.java
@@ -16,6 +16,8 @@
package com.android.car;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
@@ -52,12 +54,16 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
@SmallTest
public class VmsPublisherServiceTest {
@@ -66,8 +72,14 @@
private static final VmsLayersOffering OFFERING = new VmsLayersOffering(Collections.emptySet(),
54321);
private static final VmsLayer LAYER = new VmsLayer(1, 2, 3);
+ private static final VmsLayer LAYER2 = new VmsLayer(2, 2, 8);
+ private static final VmsLayer LAYER3 = new VmsLayer(3, 2, 8);
+ private static final VmsLayer LAYER4 = new VmsLayer(4, 2, 8);
+
private static final int PUBLISHER_ID = 54321;
private static final byte[] PAYLOAD = new byte[]{1, 2, 3, 4};
+ private static final byte[] PAYLOAD2 = new byte[]{1, 2, 3, 4, 5, 6};
+ private static final byte[] PAYLOAD3 = new byte[]{10, 12, 93, 4, 5, 6, 1, 1, 1};
@Rule
public MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -84,6 +96,10 @@
private IVmsSubscriberClient mSubscriberClient;
@Mock
private IVmsSubscriberClient mSubscriberClient2;
+ @Mock
+ private IVmsSubscriberClient mThrowingSubscriberClient;
+ @Mock
+ private IVmsSubscriberClient mThrowingSubscriberClient2;
private VmsPublisherService mPublisherService;
private MockPublisherClient mPublisherClient;
@@ -96,6 +112,7 @@
mPublisherClient2 = new MockPublisherClient();
when(mBrokerService.getSubscribersForLayerFromPublisher(LAYER, PUBLISHER_ID))
.thenReturn(new HashSet<>(Arrays.asList(mSubscriberClient, mSubscriberClient2)));
+
}
@Test
@@ -175,6 +192,15 @@
}
@Test
+ public void testPublishNullLayerAndNullPayload() throws Exception {
+ mPublisherService.onClientConnected("SomeClient", mPublisherClient.asBinder());
+
+ // We just want to ensure that no exceptions are thrown here.
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, null, PUBLISHER_ID,
+ null);
+ }
+
+ @Test
public void testPublish_ClientError() throws Exception {
mPublisherService.onClientConnected("SomeClient", mPublisherClient.asBinder());
doThrow(new RemoteException()).when(mSubscriberClient).onVmsMessageReceived(LAYER, PAYLOAD);
@@ -274,6 +300,341 @@
}
@Test
+ public void testDump_getPacketCount() throws Exception {
+ mPublisherService.onClientConnected("SomeClient", mPublisherClient.asBinder());
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ PrintWriter printWriter = new PrintWriter(outputStream);
+
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER, PUBLISHER_ID,
+ PAYLOAD);
+ mPublisherService.dump(printWriter);
+
+ printWriter.flush();
+ String dumpString = outputStream.toString();
+ String expectedPacketCountString = String.format(VmsPublisherService.PACKET_COUNT_FORMAT,
+ LAYER, 1L);
+ String expectedPacketSizeString = String.format(VmsPublisherService.PACKET_SIZE_FORMAT,
+ LAYER, PAYLOAD.length);
+ assertThat(dumpString.contains(expectedPacketCountString)).isTrue();
+ assertThat(dumpString.contains(expectedPacketSizeString)).isTrue();
+ }
+
+ @Test
+ public void testDump_getPacketCounts() throws Exception {
+ mPublisherService.onClientConnected("SomeClient", mPublisherClient.asBinder());
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ PrintWriter printWriter = new PrintWriter(outputStream);
+
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER, PUBLISHER_ID,
+ PAYLOAD);
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER2, PUBLISHER_ID,
+ PAYLOAD);
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER, PUBLISHER_ID,
+ PAYLOAD2);
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER, PUBLISHER_ID,
+ PAYLOAD);
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER3, PUBLISHER_ID,
+ PAYLOAD);
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER, PUBLISHER_ID,
+ PAYLOAD3);
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER, PUBLISHER_ID,
+ PAYLOAD3);
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER2, PUBLISHER_ID,
+ PAYLOAD3);
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER, PUBLISHER_ID,
+ PAYLOAD3);
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER3, PUBLISHER_ID,
+ PAYLOAD);
+ mPublisherService.dump(printWriter);
+
+ printWriter.flush();
+ String dumpString = outputStream.toString();
+
+ // LAYER called 6 times with PAYLOAD 2 times, PAYLOAD2 1 time, PAYLOAD3 3 times
+ String expectedPacketCountString1 = String.format(VmsPublisherService.PACKET_COUNT_FORMAT,
+ LAYER, 6L);
+ String expectedPacketSizeString1 = String.format(VmsPublisherService.PACKET_SIZE_FORMAT,
+ LAYER, 2 * PAYLOAD.length + PAYLOAD2.length + 3 * PAYLOAD3.length);
+
+ // LAYER2 called 2 times with PAYLOAD 1 time, PAYLOAD2 0 time, PAYLOAD3 1 times
+ String expectedPacketCountString2 = String.format(VmsPublisherService.PACKET_COUNT_FORMAT,
+ LAYER2, 2L);
+ String expectedPacketSizeString2 = String.format(VmsPublisherService.PACKET_SIZE_FORMAT,
+ LAYER2, PAYLOAD.length + PAYLOAD3.length);
+
+ // LAYER3 called 2 times with PAYLOAD 2 times, PAYLOAD2 0 time, PAYLOAD3 0 times
+ String expectedPacketCountString3 = String.format(VmsPublisherService.PACKET_COUNT_FORMAT,
+ LAYER3, 2L);
+ String expectedPacketSizeString3 = String.format(VmsPublisherService.PACKET_SIZE_FORMAT,
+ LAYER3, 2 * PAYLOAD.length);
+
+ assertThat(dumpString.contains(expectedPacketCountString1)).isTrue();
+ assertThat(dumpString.contains(expectedPacketSizeString1)).isTrue();
+ assertThat(dumpString.contains(expectedPacketCountString2)).isTrue();
+ assertThat(dumpString.contains(expectedPacketSizeString2)).isTrue();
+ assertThat(dumpString.contains(expectedPacketCountString3)).isTrue();
+ assertThat(dumpString.contains(expectedPacketSizeString3)).isTrue();
+ }
+
+ @Test
+ public void testDumpNoListeners_getPacketFailureCount() throws Exception {
+ mPublisherService.onClientConnected("SomeClient", mPublisherClient.asBinder());
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ PrintWriter printWriter = new PrintWriter(outputStream);
+
+ // Layer 2 has no listeners and should therefore result in a packet failure to be recorded.
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER2, PUBLISHER_ID,
+ PAYLOAD);
+ mPublisherService.dump(printWriter);
+
+ printWriter.flush();
+ String dumpString = outputStream.toString();
+
+ String expectedPacketFailureString = String.format(
+ VmsPublisherService.PACKET_FAILURE_COUNT_FORMAT,
+ LAYER2, "SomeClient", "", 1L);
+ String expectedPacketFailureSizeString = String.format(
+ VmsPublisherService.PACKET_FAILURE_SIZE_FORMAT,
+ LAYER2, "SomeClient", "", PAYLOAD.length);
+
+ assertThat(dumpString.contains(expectedPacketFailureString)).isTrue();
+ assertThat(dumpString.contains(expectedPacketFailureSizeString)).isTrue();
+ }
+
+ @Test
+ public void testDumpNoListeners_getPacketFailureCounts() throws Exception {
+ // LAYER2 and LAYER3 both have no listeners
+ when(mBrokerService.getSubscribersForLayerFromPublisher(LAYER2, PUBLISHER_ID))
+ .thenReturn(new HashSet<>());
+ when(mBrokerService.getSubscribersForLayerFromPublisher(LAYER3, PUBLISHER_ID))
+ .thenReturn(new HashSet<>());
+
+ mPublisherService.onClientConnected("SomeClient", mPublisherClient.asBinder());
+ mPublisherService.onClientConnected("SomeClient2", mPublisherClient2.asBinder());
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ PrintWriter printWriter = new PrintWriter(outputStream);
+
+ // Layer 2 has no listeners and should therefore result in a packet failure to be recorded.
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER2, PUBLISHER_ID,
+ PAYLOAD);
+ mPublisherClient2.mPublisherService.publish(mPublisherClient2.mToken, LAYER3, PUBLISHER_ID,
+ PAYLOAD);
+
+ mPublisherService.dump(printWriter);
+
+ printWriter.flush();
+ String dumpString = outputStream.toString();
+
+ String expectedPacketFailureString = String.format(
+ VmsPublisherService.PACKET_FAILURE_COUNT_FORMAT,
+ LAYER2, "SomeClient", "", 1L);
+ String expectedPacketFailureString2 = String.format(
+ VmsPublisherService.PACKET_FAILURE_COUNT_FORMAT,
+ LAYER3, "SomeClient2", "", 1L);
+ String expectedPacketFailureSizeString = String.format(
+ VmsPublisherService.PACKET_FAILURE_SIZE_FORMAT,
+ LAYER2, "SomeClient", "", PAYLOAD.length);
+ String expectedPacketFailureSizeString2 = String.format(
+ VmsPublisherService.PACKET_FAILURE_SIZE_FORMAT,
+ LAYER3, "SomeClient2", "", PAYLOAD.length);
+
+ assertThat(dumpString.contains(expectedPacketFailureString)).isTrue();
+ assertThat(dumpString.contains(expectedPacketFailureSizeString)).isTrue();
+ assertThat(dumpString.contains(expectedPacketFailureString2)).isTrue();
+ assertThat(dumpString.contains(expectedPacketFailureSizeString2)).isTrue();
+ }
+
+ @Test
+ public void testDumpRemoteException_getPacketFailureCount() throws Exception {
+ // The listener on LAYER3 will throw on LAYER3 and PAYLOAD
+ Mockito.doThrow(new RemoteException()).when(mThrowingSubscriberClient).onVmsMessageReceived(
+ LAYER3, PAYLOAD);
+ when(mBrokerService.getSubscribersForLayerFromPublisher(LAYER3, PUBLISHER_ID))
+ .thenReturn(new HashSet<>(Arrays.asList(mThrowingSubscriberClient)));
+ when(mBrokerService.getPackageName(mThrowingSubscriberClient)).thenReturn("Thrower");
+
+ mPublisherService.onClientConnected("SomeClient", mPublisherClient.asBinder());
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ PrintWriter printWriter = new PrintWriter(outputStream);
+
+ // Layer 2 has no listeners and should therefore result in a packet failure to be recorded.
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER3, PUBLISHER_ID,
+ PAYLOAD);
+
+ mPublisherService.dump(printWriter);
+
+ printWriter.flush();
+ String dumpString = outputStream.toString();
+
+ String expectedPacketFailureString = String.format(
+ VmsPublisherService.PACKET_FAILURE_COUNT_FORMAT,
+ LAYER3, "SomeClient", "Thrower", 1L);
+ String expectedPacketFailureSizeString = String.format(
+ VmsPublisherService.PACKET_FAILURE_SIZE_FORMAT,
+ LAYER3, "SomeClient", "Thrower", PAYLOAD.length);
+
+ assertThat(dumpString.contains(expectedPacketFailureString)).isTrue();
+ assertThat(dumpString.contains(expectedPacketFailureSizeString)).isTrue();
+ }
+
+ @Test
+ public void testDumpRemoteException_getPacketFailureCounts() throws Exception {
+ // The listeners will throw on LAYER3 or LAYER4 and PAYLOAD
+ Mockito.doThrow(new RemoteException()).when(mThrowingSubscriberClient).onVmsMessageReceived(
+ LAYER3, PAYLOAD);
+ Mockito.doThrow(new RemoteException()).when(mThrowingSubscriberClient).onVmsMessageReceived(
+ LAYER4, PAYLOAD);
+ Mockito.doThrow(new RemoteException()).when(
+ mThrowingSubscriberClient2).onVmsMessageReceived(LAYER3, PAYLOAD);
+ Mockito.doThrow(new RemoteException()).when(
+ mThrowingSubscriberClient2).onVmsMessageReceived(LAYER4, PAYLOAD);
+
+ when(mBrokerService.getSubscribersForLayerFromPublisher(LAYER3, PUBLISHER_ID))
+ .thenReturn(new HashSet<>(
+ Arrays.asList(mThrowingSubscriberClient, mThrowingSubscriberClient2)));
+ when(mBrokerService.getSubscribersForLayerFromPublisher(LAYER4, PUBLISHER_ID))
+ .thenReturn(new HashSet<>(
+ Arrays.asList(mThrowingSubscriberClient, mThrowingSubscriberClient2)));
+
+ when(mBrokerService.getPackageName(mThrowingSubscriberClient)).thenReturn("Thrower");
+ when(mBrokerService.getPackageName(mThrowingSubscriberClient2)).thenReturn("Thrower2");
+
+ mPublisherService.onClientConnected("SomeClient", mPublisherClient.asBinder());
+ mPublisherService.onClientConnected("SomeClient2", mPublisherClient2.asBinder());
+
+ // Layer 2 has no listeners and should therefore result in a packet failure to be recorded.
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER3, PUBLISHER_ID,
+ PAYLOAD);
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER3, PUBLISHER_ID,
+ PAYLOAD);
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER4, PUBLISHER_ID,
+ PAYLOAD);
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER4, PUBLISHER_ID,
+ PAYLOAD);
+
+ mPublisherClient2.mPublisherService.publish(mPublisherClient2.mToken, LAYER3, PUBLISHER_ID,
+ PAYLOAD);
+ mPublisherClient2.mPublisherService.publish(mPublisherClient2.mToken, LAYER3, PUBLISHER_ID,
+ PAYLOAD);
+ mPublisherClient2.mPublisherService.publish(mPublisherClient2.mToken, LAYER4, PUBLISHER_ID,
+ PAYLOAD);
+ mPublisherClient2.mPublisherService.publish(mPublisherClient2.mToken, LAYER4, PUBLISHER_ID,
+ PAYLOAD);
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ PrintWriter printWriter = new PrintWriter(outputStream);
+ mPublisherService.dump(printWriter);
+
+ printWriter.flush();
+ String dumpString = outputStream.toString();
+
+ List<String> expectedStrings = Arrays.asList(
+ String.format(VmsPublisherService.PACKET_FAILURE_COUNT_FORMAT, LAYER3, "SomeClient",
+ "Thrower", 2L),
+ String.format(VmsPublisherService.PACKET_FAILURE_COUNT_FORMAT, LAYER3, "SomeClient",
+ "Thrower2", 2L),
+ String.format(VmsPublisherService.PACKET_FAILURE_COUNT_FORMAT, LAYER4, "SomeClient",
+ "Thrower", 2L),
+ String.format(VmsPublisherService.PACKET_FAILURE_COUNT_FORMAT, LAYER4, "SomeClient",
+ "Thrower2", 2L),
+ String.format(VmsPublisherService.PACKET_FAILURE_COUNT_FORMAT, LAYER3,
+ "SomeClient2",
+ "Thrower", 2L),
+ String.format(VmsPublisherService.PACKET_FAILURE_COUNT_FORMAT, LAYER3,
+ "SomeClient2",
+ "Thrower2", 2L),
+ String.format(VmsPublisherService.PACKET_FAILURE_COUNT_FORMAT, LAYER4,
+ "SomeClient2",
+ "Thrower", 2L),
+ String.format(VmsPublisherService.PACKET_FAILURE_COUNT_FORMAT, LAYER4,
+ "SomeClient2",
+ "Thrower2", 2L),
+
+ String.format(VmsPublisherService.PACKET_FAILURE_SIZE_FORMAT, LAYER3, "SomeClient",
+ "Thrower", 2 * PAYLOAD.length),
+ String.format(VmsPublisherService.PACKET_FAILURE_SIZE_FORMAT, LAYER3, "SomeClient",
+ "Thrower2", 2 * PAYLOAD.length),
+ String.format(VmsPublisherService.PACKET_FAILURE_SIZE_FORMAT, LAYER4, "SomeClient",
+ "Thrower", 2 * PAYLOAD.length),
+ String.format(VmsPublisherService.PACKET_FAILURE_SIZE_FORMAT, LAYER4, "SomeClient",
+ "Thrower2", 2 * PAYLOAD.length),
+ String.format(VmsPublisherService.PACKET_FAILURE_SIZE_FORMAT, LAYER3, "SomeClient2",
+ "Thrower", 2 * PAYLOAD.length),
+ String.format(VmsPublisherService.PACKET_FAILURE_SIZE_FORMAT, LAYER3, "SomeClient2",
+ "Thrower2", 2 * PAYLOAD.length),
+ String.format(VmsPublisherService.PACKET_FAILURE_SIZE_FORMAT, LAYER4, "SomeClient2",
+ "Thrower", 2 * PAYLOAD.length),
+ String.format(VmsPublisherService.PACKET_FAILURE_SIZE_FORMAT, LAYER4, "SomeClient2",
+ "Thrower2", 2 * PAYLOAD.length));
+
+ for (String expected : expectedStrings) {
+ assertThat(dumpString.contains(expected)).isTrue();
+ }
+ }
+
+ @Test
+ public void testDump_getAllMetrics() throws Exception {
+
+ // LAYER3 has no subscribers
+ when(mBrokerService.getSubscribersForLayerFromPublisher(LAYER3, PUBLISHER_ID))
+ .thenReturn(new HashSet<>(Arrays.asList()));
+
+ // LAYER4 has a subscriber that will always throw
+ Mockito.doThrow(new RemoteException()).when(mThrowingSubscriberClient).onVmsMessageReceived(
+ LAYER4, PAYLOAD);
+
+ when(mBrokerService.getSubscribersForLayerFromPublisher(LAYER4, PUBLISHER_ID))
+ .thenReturn(new HashSet<>(
+ Arrays.asList(mThrowingSubscriberClient)));
+
+ when(mBrokerService.getPackageName(mThrowingSubscriberClient)).thenReturn("Thrower");
+
+ mPublisherService.onClientConnected("SomeClient", mPublisherClient.asBinder());
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER, PUBLISHER_ID,
+ PAYLOAD);
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER, PUBLISHER_ID,
+ PAYLOAD2);
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER3, PUBLISHER_ID,
+ PAYLOAD3);
+ mPublisherClient.mPublisherService.publish(mPublisherClient.mToken, LAYER4, PUBLISHER_ID,
+ PAYLOAD);
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ PrintWriter printWriter = new PrintWriter(outputStream);
+ mPublisherService.dump(printWriter);
+
+ printWriter.flush();
+ String dumpString = outputStream.toString();
+
+ List<String> expectedStrings = Arrays.asList(
+ String.format(VmsPublisherService.PACKET_COUNT_FORMAT, LAYER, 2),
+ String.format(VmsPublisherService.PACKET_COUNT_FORMAT, LAYER3, 1),
+ String.format(VmsPublisherService.PACKET_COUNT_FORMAT, LAYER4, 1),
+ String.format(VmsPublisherService.PACKET_SIZE_FORMAT, LAYER,
+ PAYLOAD.length + PAYLOAD2.length),
+ String.format(VmsPublisherService.PACKET_SIZE_FORMAT, LAYER3, PAYLOAD3.length),
+ String.format(VmsPublisherService.PACKET_SIZE_FORMAT, LAYER4, PAYLOAD.length),
+ String.format(VmsPublisherService.PACKET_FAILURE_COUNT_FORMAT, LAYER3, "SomeClient",
+ "",
+ 1),
+ String.format(VmsPublisherService.PACKET_FAILURE_SIZE_FORMAT, LAYER3, "SomeClient",
+ "",
+ PAYLOAD3.length),
+ String.format(VmsPublisherService.PACKET_FAILURE_COUNT_FORMAT, LAYER4, "SomeClient",
+ "Thrower", 1),
+ String.format(VmsPublisherService.PACKET_FAILURE_SIZE_FORMAT, LAYER4, "SomeClient",
+ "Thrower", PAYLOAD.length)
+ );
+
+ for (String expected : expectedStrings) {
+ assertThat(dumpString.contains(expected)).isTrue();
+ }
+ }
+
+
+ @Test
public void testRelease() {
mPublisherService.release();
verify(mClientManager).unregisterConnectionListener(mPublisherService);