diff options
author | Asaf Rosenfeld <asafro@google.com> | 2017-05-19 12:59:56 -0700 |
---|---|---|
committer | Asaf Rosenfeld <asafro@google.com> | 2017-05-25 09:08:41 -0700 |
commit | 602848940066abb3f0c36148cbe09c3d1855dffc (patch) | |
tree | 31e5cbae1dda585399b99ab7534dc5b77b1f0469 | |
parent | 9a8bb73e9784e124dbb4f29cacb568be38b6cf78 (diff) | |
download | Car-602848940066abb3f0c36148cbe09c3d1855dffc.tar.gz |
Adding Publisher ID support to non-hal clients.
Publishers send a serialized proto message with their description and get ID integer.
Everytime the same serialized message is sent they will get the same ID so it is
persistent within client crashes.
Other clients can ask for a map all the IDs with their serialized descriptions.
Test: All of VMS tests pass + added unit tests and integration tests.
Change-Id: I025dd9943427ee8631a6a00adb0f9a17334a821e
Bugs: 38185290, 38185731, 38185927
11 files changed, 322 insertions, 27 deletions
diff --git a/car-lib/src/android/car/vms/IVmsPublisherService.aidl b/car-lib/src/android/car/vms/IVmsPublisherService.aidl index 26b6e523ed..3312794e9a 100644 --- a/car-lib/src/android/car/vms/IVmsPublisherService.aidl +++ b/car-lib/src/android/car/vms/IVmsPublisherService.aidl @@ -40,4 +40,11 @@ interface IVmsPublisherService { * 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 a static ID. Between reboots, subsequent calls with the same publisher info will + * return the same ID so that a restarting process can obtain the same ID as it had before. + */ + int getPublisherStaticId(in byte[] publisherInfo) = 3; } diff --git a/car-lib/src/android/car/vms/IVmsSubscriberService.aidl b/car-lib/src/android/car/vms/IVmsSubscriberService.aidl index 236ae5a94c..923413478d 100644 --- a/car-lib/src/android/car/vms/IVmsSubscriberService.aidl +++ b/car-lib/src/android/car/vms/IVmsSubscriberService.aidl @@ -52,8 +52,12 @@ interface IVmsSubscriberService { in IVmsSubscriberClient listener) = 3; /** - * Tells the VmsSubscriberService a client requests the list of available layers. - * The service should call the client's onLayersAvailabilityChange in response. + * Returns a list of available layers from the closure of the publishers offerings. */ List<VmsLayer> getAvailableLayers() = 4; + + /** + * Returns a the publisher information for a publisher ID. + */ + byte[] getPublisherInfo(in int publisherId) = 5; } diff --git a/car-lib/src/android/car/vms/VmsPublisherClientService.java b/car-lib/src/android/car/vms/VmsPublisherClientService.java index 85fd2c2b8d..ea265dfe5e 100644 --- a/car-lib/src/android/car/vms/VmsPublisherClientService.java +++ b/car-lib/src/android/car/vms/VmsPublisherClientService.java @@ -155,6 +155,23 @@ public abstract class VmsPublisherClientService extends Service { return token; } + public final int getPublisherStaticId(byte[] publisherInfo) { + if (mVmsPublisherService == null) { + throw new IllegalStateException("VmsPublisherService not set."); + } + Integer publisherStaticId = null; + try { + Log.i(TAG, "Getting publisher static ID"); + publisherStaticId = mVmsPublisherService.getPublisherStaticId(publisherInfo); + } catch (RemoteException e) { + Log.e(TAG, "unable to invoke binder method.", e); + } + if (publisherStaticId == null) { + throw new IllegalStateException("VmsPublisherService cannot get a publisher static ID."); + } + return publisherStaticId; + } + /** * Uses the VmsPublisherService binder to get the list of layer/version that have any * subscribers. diff --git a/car-lib/src/android/car/vms/VmsSubscriberManager.java b/car-lib/src/android/car/vms/VmsSubscriberManager.java index 640948a056..84405f4a12 100644 --- a/car-lib/src/android/car/vms/VmsSubscriberManager.java +++ b/car-lib/src/android/car/vms/VmsSubscriberManager.java @@ -157,6 +157,24 @@ public final class VmsSubscriberManager implements CarManagerBase { } /** + * Returns a serialized publisher information for a publisher ID. + */ + public byte[] getPublisherInfo(int publisherId) throws CarNotConnectedException, IllegalStateException { + if (DBG) { + Log.d(TAG, "Getting all publishers info."); + } + 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. diff --git a/service/src/com/android/car/VmsPublisherService.java b/service/src/com/android/car/VmsPublisherService.java index 8bb0167d8d..3ecbfacb76 100644 --- a/service/src/com/android/car/VmsPublisherService.java +++ b/service/src/com/android/car/VmsPublisherService.java @@ -135,6 +135,12 @@ public class VmsPublisherService extends IVmsPublisherService.Stub return mHal.getSubscriptionState(); } + @Override + public int getPublisherStaticId(byte[] publisherInfo) { + ICarImpl.assertVmsPublisherPermission(mContext); + return mHal.getPublisherStaticId(publisherInfo); + } + // Implements VmsHalListener interface /** * This method is only invoked by VmsHalService.notifyPublishers which is synchronized. diff --git a/service/src/com/android/car/VmsPublishersInfo.java b/service/src/com/android/car/VmsPublishersInfo.java new file mode 100644 index 0000000000..04ee82f78d --- /dev/null +++ b/service/src/com/android/car/VmsPublishersInfo.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.car; + + +import android.car.annotation.FutureFeature; +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; + +@FutureFeature +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/VmsSubscriberService.java b/service/src/com/android/car/VmsSubscriberService.java index eabb0a6a1a..fc0a88557e 100644 --- a/service/src/com/android/car/VmsSubscriberService.java +++ b/service/src/com/android/car/VmsSubscriberService.java @@ -262,6 +262,13 @@ public class VmsSubscriberService extends IVmsSubscriberService.Stub } @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(); diff --git a/service/src/com/android/car/hal/VmsHalService.java b/service/src/com/android/car/hal/VmsHalService.java index b29498500b..8ab5427a39 100644 --- a/service/src/com/android/car/hal/VmsHalService.java +++ b/service/src/com/android/car/hal/VmsHalService.java @@ -37,6 +37,7 @@ 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; @@ -71,13 +72,13 @@ public class VmsHalService extends HalServiceBase { private final IBinder mHalPublisherToken = new Binder(); private final VehicleHal mVehicleHal; - private final Object mRoutingLock = new Object(); + private final Object mLock = new Object(); private final VmsRouting mRouting = new VmsRouting(); - private final Object mAvailabilityLock = new Object(); - @GuardedBy("mAvailabilityLock") + @GuardedBy("mLock") private final Map<IBinder, VmsLayersOffering> mOfferings = new HashMap<>(); - @GuardedBy("mAvailabilityLock") + @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. @@ -125,7 +126,7 @@ public class VmsHalService extends HalServiceBase { public void addSubscription(IVmsSubscriberClient listener, VmsLayer layer) { boolean firstSubscriptionForLayer = false; - synchronized (mRoutingLock) { + synchronized (mLock) { // Check if publishers need to be notified about this change in subscriptions. firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer); @@ -139,7 +140,7 @@ public class VmsHalService extends HalServiceBase { public void removeSubscription(IVmsSubscriberClient listener, VmsLayer layer) { boolean layerHasSubscribers = true; - synchronized (mRoutingLock) { + synchronized (mLock) { if (!mRouting.hasLayerSubscriptions(layer)) { Log.i(TAG, "Trying to remove a layer with no subscription: " + layer); return; @@ -157,50 +158,74 @@ public class VmsHalService extends HalServiceBase { } public void addSubscription(IVmsSubscriberClient listener) { - synchronized (mRoutingLock) { + synchronized (mLock) { mRouting.addSubscription(listener); } } public void removeSubscription(IVmsSubscriberClient listener) { - synchronized (mRoutingLock) { + synchronized (mLock) { mRouting.removeSubscription(listener); } } public void removeDeadListener(IVmsSubscriberClient listener) { - synchronized (mRoutingLock) { + synchronized (mLock) { mRouting.removeDeadListener(listener); } } public Set<IVmsSubscriberClient> getListeners(VmsLayer layer) { - synchronized (mRoutingLock) { + synchronized (mLock) { return mRouting.getListeners(layer); } } public Set<IVmsSubscriberClient> getAllListeners() { - synchronized (mRoutingLock) { + synchronized (mLock) { return mRouting.getAllListeners(); } } public boolean isHalSubscribed(VmsLayer layer) { - synchronized (mRoutingLock) { + synchronized (mLock) { return mRouting.isHalSubscribed(layer); } } public VmsSubscriptionState getSubscriptionState() { - synchronized (mRoutingLock) { + 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 getPublisherStaticId(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); + } + } + public void addHalSubscription(VmsLayer layer) { boolean firstSubscriptionForLayer = true; - synchronized (mRoutingLock) { + synchronized (mLock) { // Check if publishers need to be notified about this change in subscriptions. firstSubscriptionForLayer = !mRouting.hasLayerSubscriptions(layer); @@ -214,7 +239,7 @@ public class VmsHalService extends HalServiceBase { public void removeHalSubscription(VmsLayer layer) { boolean layerHasSubscribers = true; - synchronized (mRoutingLock) { + synchronized (mLock) { if (!mRouting.hasLayerSubscriptions(layer)) { Log.i(TAG, "Trying to remove a layer with no subscription: " + layer); return; @@ -232,14 +257,14 @@ public class VmsHalService extends HalServiceBase { } public boolean containsListener(IVmsSubscriberClient listener) { - synchronized (mRoutingLock) { + synchronized (mLock) { return mRouting.containsListener(listener); } } public void setPublisherLayersOffering(IBinder publisherToken, VmsLayersOffering offering){ Set<VmsLayer> availableLayers = Collections.EMPTY_SET; - synchronized (mAvailabilityLock) { + synchronized (mLock) { updateOffering(publisherToken, offering); availableLayers = mAvailableLayers.getAvailableLayers(); } @@ -248,7 +273,7 @@ public class VmsHalService extends HalServiceBase { public Set<VmsLayer> getAvailableLayers() { //TODO(b/36872877): wrap available layers in VmsAvailabilityState similar to VmsSubscriptionState. - synchronized (mAvailabilityLock) { + synchronized (mLock) { return mAvailableLayers.getAvailableLayers(); } } @@ -475,7 +500,7 @@ public class VmsHalService extends HalServiceBase { } // Store the HAL offering. VmsLayersOffering offering = new VmsLayersOffering(offeredLayers); - synchronized (mAvailabilityLock) { + synchronized (mLock) { updateOffering(mHalPublisherToken, offering); } } @@ -489,7 +514,7 @@ public class VmsHalService extends HalServiceBase { * </ul> */ private void handleAvailabilityEvent() { - synchronized (mAvailabilityLock) { + synchronized (mLock) { Collection<VmsLayer> availableLayers = mAvailableLayers.getAvailableLayers(); VehiclePropValue vehiclePropertyValue = toVehiclePropValue( VmsMessageType.AVAILABILITY_RESPONSE, availableLayers); @@ -527,7 +552,7 @@ public class VmsHalService extends HalServiceBase { private void updateOffering(IBinder publisherToken, VmsLayersOffering offering) { Set<VmsLayer> availableLayers = Collections.EMPTY_SET; - synchronized (mAvailabilityLock) { + synchronized (mLock) { mOfferings.put(publisherToken, offering); // Update layers availability. diff --git a/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java b/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java index 63492f83b1..d8e344b4be 100644 --- a/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java +++ b/tests/carservice_test/src/com/android/car/test/VmsPublisherClientMockService.java @@ -22,6 +22,7 @@ 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.List; import java.util.ArrayList; @@ -35,18 +36,24 @@ import java.util.ArrayList; */ @FutureFeature 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. - publishIfNeeded(subscriptionState); - declareOffering(subscriptionState); + 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"); + getPublisherStaticId(VmsPublisherSubscriberTest.PAYLOAD); publishIfNeeded(subscriptionState); declareOffering(subscriptionState); } diff --git a/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java b/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java index 20fa0b6476..3b3a94fc48 100644 --- a/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java +++ b/tests/carservice_test/src/com/android/car/test/VmsPublisherSubscriberTest.java @@ -29,13 +29,12 @@ import android.content.res.Resources; import android.hardware.automotive.vehicle.V2_0.VehiclePropertyAccess; import android.hardware.automotive.vehicle.V2_0.VehiclePropertyChangeMode; import android.hardware.automotive.vehicle.V2_1.VehicleProperty; - import com.android.car.vehiclehal.test.MockedVehicleHal; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; @@ -43,6 +42,7 @@ import java.util.concurrent.TimeUnit; public class VmsPublisherSubscriberTest extends MockedCarTestBase { private static final int LAYER_ID = 88; private static final int LAYER_VERSION = 19; + private static final int EXPECTED_PUBLISHER_ID = 0; private static final String TAG = "VmsPubSubTest"; public static final VmsLayer LAYER = new VmsLayer(LAYER_ID, LAYER_VERSION); @@ -127,6 +127,27 @@ public class VmsPublisherSubscriberTest extends MockedCarTestBase { } /** + * 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 { + if (!VmsTestUtils.canRunTest(TAG)) return; + VmsSubscriberManager vmsSubscriberManager = (VmsSubscriberManager) getCar().getCarManager( + Car.VMS_SUBSCRIBER_SERVICE); + // Subscribe to layer as a way to make sure the mock client completed setting the information. + TestListener listener = new TestListener(); + vmsSubscriberManager.setListener(listener); + 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, so in this * test the listener subscribes to a layer and verifies that it gets the notification that it * is available. 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 0000000000..2b75012950 --- /dev/null +++ b/tests/carservice_unit_test/src/com/android/car/VmsPublishersInfoTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.car; + +import android.car.annotation.FutureFeature; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +import java.util.Arrays; +import java.util.Map; + +@FutureFeature +@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); + } +} |