Merge "softap: Update LOHS API usage"
diff --git a/car-lib/Android.bp b/car-lib/Android.bp
index 73cbc5d..adec1a5 100644
--- a/car-lib/Android.bp
+++ b/car-lib/Android.bp
@@ -311,6 +311,9 @@
},
},
compile_dex: true,
+ dist: {
+ targets: ["dist_files"],
+ }
}
// Export the api/system-current.txt file.
diff --git a/car-lib/api/system-current.txt b/car-lib/api/system-current.txt
index 6310b72..4388c08 100644
--- a/car-lib/api/system-current.txt
+++ b/car-lib/api/system-current.txt
@@ -234,7 +234,8 @@
public abstract class InstrumentClusterRenderingService extends android.app.Service {
ctor public InstrumentClusterRenderingService();
method @Deprecated @Nullable public android.graphics.Bitmap getBitmap(android.net.Uri);
- method @Nullable public android.graphics.Bitmap getBitmap(android.net.Uri, int, int);
+ method @Nullable public android.graphics.Bitmap getBitmap(@NonNull android.net.Uri, int, int);
+ method @Nullable public android.graphics.Bitmap getBitmap(@NonNull android.net.Uri, int, int, float);
method @MainThread @Nullable public abstract android.car.cluster.renderer.NavigationRenderer getNavigationRenderer();
method @CallSuper public android.os.IBinder onBind(android.content.Intent);
method @MainThread public void onKeyEvent(@NonNull android.view.KeyEvent);
@@ -816,6 +817,7 @@
method @RequiresPermission(android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) public int getGroupMinVolume(int, int);
method @RequiresPermission(android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) public int getGroupVolume(int);
method @RequiresPermission(android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) public int getGroupVolume(int, int);
+ method @NonNull @RequiresPermission(android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) public java.util.List<android.media.AudioDeviceInfo> getInputDevicesForZoneId(int);
method @Nullable @RequiresPermission(android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) public android.media.AudioDeviceInfo getOutputDeviceForUsage(int, int);
method @NonNull @RequiresPermission(android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) public int[] getUsagesForVolumeGroupId(int);
method @NonNull @RequiresPermission(android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) public int[] getUsagesForVolumeGroupId(int, int);
@@ -928,6 +930,14 @@
}
+package android.car.settings {
+
+ public static final class CarSettings.Secure {
+ field public static final String KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL = "android.car.KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL";
+ }
+
+}
+
package android.car.storagemonitoring {
public final class CarStorageMonitoringManager {
@@ -1094,21 +1104,6 @@
}
-package android.car.user {
-
- public final class CarUserManager {
- method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int createDriver(@NonNull String, boolean);
- method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int createPassenger(@NonNull String, int);
- method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<java.lang.Integer> getAllDrivers();
- method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<java.lang.Integer> getPassengers(int);
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean startPassenger(int, int);
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean stopPassenger(int);
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean switchDriver(int);
- field public static final int INVALID_USER_ID = -10000; // 0xffffd8f0
- }
-
-}
-
package android.car.vms {
public final class VmsAssociatedLayer implements android.os.Parcelable {
diff --git a/car-lib/src/android/car/Car.java b/car-lib/src/android/car/Car.java
index f31581b..4a8a76b 100644
--- a/car-lib/src/android/car/Car.java
+++ b/car-lib/src/android/car/Car.java
@@ -158,6 +158,7 @@
/**
* @hide
*/
+ @OptionalFeature
@SystemApi
public static final String DIAGNOSTIC_SERVICE = "diagnostic";
diff --git a/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
index 3683b19..582a872 100644
--- a/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
+++ b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
@@ -96,6 +96,7 @@
private static final String BITMAP_QUERY_WIDTH = "w";
private static final String BITMAP_QUERY_HEIGHT = "h";
+ private static final String BITMAP_QUERY_OFFLANESALPHA = "offLanesAlpha";
private final Handler mUiHandler = new Handler(Looper.getMainLooper());
@@ -584,6 +585,8 @@
* This is a costly operation. Returned bitmaps should be cached and fetching should be done on
* a secondary thread.
*
+ * @param uri The URI of the bitmap
+ *
* @throws IllegalArgumentException if {@code uri} does not have width and height query params.
*
* @deprecated Replaced by {@link #getBitmap(Uri, int, int)}.
@@ -639,8 +642,16 @@
}
/**
+ * See {@link #getBitmap(Uri, int, int, float)}
+ */
+ @Nullable
+ public Bitmap getBitmap(@NonNull Uri uri, int width, int height) {
+ return getBitmap(uri, width, height, 1f);
+ }
+
+ /**
* Fetches a bitmap from the navigation context owner (application holding navigation focus)
- * of the given width and height. The fetched bitmaps are cached.
+ * of the given width and height and off lane opacity. The fetched bitmaps are cached.
* It returns null if:
* <ul>
* <li>there is no navigation context owner
@@ -649,13 +660,20 @@
* </ul>
* This is a costly operation. Returned bitmaps should be fetched on a secondary thread.
*
- * @throws IllegalArgumentException if {@code width} or {@code height} is not greater than 0.
+ * @param uri The URI of the bitmap
+ * @param width Requested width
+ * @param height Requested height
+ * @param offLanesAlpha Opacity value of the off-lane images. Only used for lane guidance images
+ * @throws IllegalArgumentException if width, height <= 0, or 0 > offLanesAlpha > 1
*/
@Nullable
- public Bitmap getBitmap(Uri uri, int width, int height) {
+ public Bitmap getBitmap(@NonNull Uri uri, int width, int height, float offLanesAlpha) {
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException("Width and height must be > 0");
}
+ if (offLanesAlpha < 0 || offLanesAlpha > 1) {
+ throw new IllegalArgumentException("offLanesAlpha must be between [0, 1]");
+ }
try {
ContextOwner contextOwner = getNavigationContextOwner();
@@ -667,6 +685,7 @@
uri = uri.buildUpon()
.appendQueryParameter(BITMAP_QUERY_WIDTH, String.valueOf(width))
.appendQueryParameter(BITMAP_QUERY_HEIGHT, String.valueOf(height))
+ .appendQueryParameter(BITMAP_QUERY_OFFLANESALPHA, String.valueOf(offLanesAlpha))
.build();
String host = uri.getHost();
diff --git a/car-lib/src/android/car/media/CarAudioManager.java b/car-lib/src/android/car/media/CarAudioManager.java
index 61cdd2f..16eaa07 100644
--- a/car-lib/src/android/car/media/CarAudioManager.java
+++ b/car-lib/src/android/car/media/CarAudioManager.java
@@ -24,8 +24,10 @@
import android.car.CarLibLog;
import android.car.CarManagerBase;
import android.media.AudioAttributes;
+import android.media.AudioDeviceAddress;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
+import android.media.AudioManager.AudioDeviceRole;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -34,7 +36,10 @@
import android.view.DisplayAddress;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* APIs for handling audio in a car.
@@ -452,7 +457,7 @@
}
return zoneIdList;
} catch (RemoteException e) {
- return handleRemoteExceptionFromCarService(e, new ArrayList<>());
+ return handleRemoteExceptionFromCarService(e, Collections.emptyList());
}
}
@@ -600,6 +605,24 @@
}
}
+ /**
+ * Gets the input devices for an audio zone
+ *
+ * @return list of input devices
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
+ public @NonNull List<AudioDeviceInfo> getInputDevicesForZoneId(int zoneId) {
+ try {
+ return convertInputDeviceAddressesToDeviceInfos(
+ mService.getInputDevicesAddressesForZoneId(zoneId),
+ AudioManager.GET_DEVICES_INPUTS);
+ } catch (RemoteException e) {
+ return handleRemoteExceptionFromCarService(e, new ArrayList<>());
+ }
+ }
+
/** @hide */
@Override
public void onCarDisconnected() {
@@ -641,6 +664,25 @@
mCarVolumeCallbacks.remove(callback);
}
+ private List<AudioDeviceInfo> convertInputDeviceAddressesToDeviceInfos(
+ List<AudioDeviceAddress> addresses, @AudioDeviceRole int flag) {
+ int addressesSize = addresses.size();
+ Set<String> deviceAddressMap = new HashSet<>(addressesSize);
+ for (int i = 0; i < addressesSize; ++i) {
+ AudioDeviceAddress deviceAddress = addresses.get(i);
+ deviceAddressMap.add(deviceAddress.getAddress());
+ }
+ List<AudioDeviceInfo> deviceInfoList = new ArrayList<>(addresses.size());
+ AudioDeviceInfo[] inputDevices = mAudioManager.getDevices(flag);
+ for (int i = 0; i < inputDevices.length; ++i) {
+ AudioDeviceInfo info = inputDevices[i];
+ if (info.isSource() && deviceAddressMap.contains(info.getAddress())) {
+ deviceInfoList.add(info);
+ }
+ }
+ return deviceInfoList;
+ }
+
/**
* Callback interface to receive volume change events in a car.
* Extend this class and register it with {@link #registerCarVolumeCallback(CarVolumeCallback)}
diff --git a/car-lib/src/android/car/media/ICarAudio.aidl b/car-lib/src/android/car/media/ICarAudio.aidl
index 22da23a..bc5c902 100644
--- a/car-lib/src/android/car/media/ICarAudio.aidl
+++ b/car-lib/src/android/car/media/ICarAudio.aidl
@@ -17,6 +17,7 @@
package android.car.media;
import android.car.media.CarAudioPatchHandle;
+import android.media.AudioDeviceAddress;
/**
* Binder interface for {@link android.car.media.CarAudioManager}.
* Check {@link android.car.media.CarAudioManager} APIs for expected behavior of each call.
@@ -51,6 +52,7 @@
String getOutputDeviceAddressForUsage(int zoneId, int usage);
+ List<AudioDeviceAddress> getInputDevicesAddressesForZoneId(int zoneId);
/**
* IBinder is ICarVolumeCallback but passed as IBinder due to aidl hidden.
*/
diff --git a/car-lib/src/android/car/settings/CarSettings.java b/car-lib/src/android/car/settings/CarSettings.java
index be89bdc..ca9c6ac 100644
--- a/car-lib/src/android/car/settings/CarSettings.java
+++ b/car-lib/src/android/car/settings/CarSettings.java
@@ -16,6 +16,8 @@
package android.car.settings;
+import android.annotation.SystemApi;
+
/**
* System level car related settings.
*/
@@ -97,7 +99,29 @@
/**
* @hide
*/
+ @SystemApi
public static final class Secure {
+ private Secure() {}
+
+ /**
+ * Key to indicate whether audio focus requests for
+ * {@link android.hardware.automotive.audiocontrol.V1_0.ContextNumber.NAVIGATION} should
+ * be rejected if focus is currently held by
+ * {@link android.hardware.automotive.audiocontrol.V1_0.ContextNumber.CALL}.
+ * <p>The value is a boolean (1 or 0) where:
+ * <ul>
+ * <li>1 indicates {@code NAVIGATION} should be rejected when a {@code CALL} is in progress.
+ * <li>0 indicates {@code NAVIGATION} and {@code CALL} should be allowed to hold focus
+ * concurrently.
+ * </ul>
+ *
+ * <p>Recommended {@code false} as default value.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL =
+ "android.car.KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL";
/**
* Key for a list of devices to automatically connect on Bluetooth A2DP Sink profile
diff --git a/car-lib/src/android/car/user/CarUserManager.java b/car-lib/src/android/car/user/CarUserManager.java
index 6fa2592..94a8b33 100644
--- a/car-lib/src/android/car/user/CarUserManager.java
+++ b/car-lib/src/android/car/user/CarUserManager.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
import android.annotation.UserIdInt;
import android.car.Car;
import android.car.CarManagerBase;
@@ -40,7 +39,6 @@
*
* @hide
*/
-@SystemApi
public final class CarUserManager extends CarManagerBase {
/* User id representing invalid user */
@@ -49,7 +47,6 @@
private static final String TAG = CarUserManager.class.getSimpleName();
private final ICarUserService mService;
- /** @hide */
@VisibleForTesting
public CarUserManager(Car car, @NonNull IBinder service) {
super(car);
@@ -63,10 +60,7 @@
* @param admin Whether the created driver will be an admin.
* @return user id of the created driver, or {@code INVALID_USER_ID} if the driver could
* not be created.
- *
- * @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
@Nullable
public int createDriver(@NonNull String name, boolean admin) {
@@ -85,10 +79,7 @@
* @param driverId User id of the driver under whom a passenger is created.
* @return user id of the created passenger, or {@code INVALID_USER_ID} if the passenger
* could not be created.
- *
- * @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
@Nullable
public int createPassenger(@NonNull String name, @UserIdInt int driverId) {
@@ -105,10 +96,7 @@
*
* @param driverId User id of the driver to switch to.
* @return {@code true} if user switching succeeds, or {@code false} if it fails.
- *
- * @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public boolean switchDriver(@UserIdInt int driverId) {
try {
@@ -122,10 +110,7 @@
* Returns all drivers who can occupy the driving zone. Guest users are included in the list.
*
* @return the list of user ids who can be a driver on the device.
- *
- * @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
@NonNull
public List<Integer> getAllDrivers() {
@@ -141,10 +126,7 @@
*
* @param driverId User id of a driver.
* @return the list of user ids who are passengers under the given driver.
- *
- * @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
@NonNull
public List<Integer> getPassengers(@UserIdInt int driverId) {
@@ -162,10 +144,7 @@
* @param zoneId Zone id to which the passenger is assigned.
* @return {@code true} if the user is successfully started or the user is already running.
* Otherwise, {@code false}.
- *
- * @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public boolean startPassenger(@UserIdInt int passengerId, int zoneId) {
try {
@@ -180,10 +159,7 @@
*
* @param passengerId User id of the passenger to be stopped.
* @return {@code true} if successfully stopped, or {@code false} if failed.
- *
- * @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
public boolean stopPassenger(@UserIdInt int passengerId) {
try {
@@ -193,7 +169,6 @@
}
}
- /** @hide */
@Override
public void onCarDisconnected() {
// nothing to do
diff --git a/car_product/build/car.mk b/car_product/build/car.mk
index 4814f9a..4a76b5c 100644
--- a/car_product/build/car.mk
+++ b/car_product/build/car.mk
@@ -101,6 +101,7 @@
libcar-framework-service-jni \
# System Server components
+# Order is important: if X depends on Y, then Y should precede X on the list.
PRODUCT_SYSTEM_SERVER_JARS += car-frameworks-service
# Boot animation
@@ -184,8 +185,6 @@
zh_CN zh_HK zh_TW \
zu_ZA
-# should add to BOOT_JARS only once
-ifeq (,$(INCLUDED_ANDROID_CAR_TO_PRODUCT_BOOT_JARS))
PRODUCT_BOOT_JARS += \
android.car
@@ -198,9 +197,6 @@
PRODUCT_HIDDENAPI_STUBS_TEST := \
android.car-test-stubs-dex
-INCLUDED_ANDROID_CAR_TO_PRODUCT_BOOT_JARS := yes
-endif
-
# Disable Prime Shader Cache in SurfaceFlinger to make it available faster
PRODUCT_PROPERTY_OVERRIDES += \
service.sf.prime_shader_cache=0
diff --git a/computepipe/aidl/android/automotive/computepipe/registry/IClientInfo.aidl b/computepipe/aidl/android/automotive/computepipe/registry/IClientInfo.aidl
index d01ef0a..1dd2010 100644
--- a/computepipe/aidl/android/automotive/computepipe/registry/IClientInfo.aidl
+++ b/computepipe/aidl/android/automotive/computepipe/registry/IClientInfo.aidl
@@ -18,7 +18,7 @@
@VintfStability
interface IClientInfo {
/**
- * Retrieve the ID assigned to the client
+ * Retrieve the name of the client.
*/
- int getClientId();
+ @utf8InCpp String getClientName();
}
diff --git a/computepipe/example/FaceTracker.h b/computepipe/example/FaceTracker.h
index ea6ba8b..60fd473 100644
--- a/computepipe/example/FaceTracker.h
+++ b/computepipe/example/FaceTracker.h
@@ -57,9 +57,9 @@
class ClientInfo : public BnClientInfo {
public:
- ndk::ScopedAStatus getClientId(int32_t* _aidl_return) {
+ ndk::ScopedAStatus getClientName(std::string* _aidl_return) override {
if (_aidl_return) {
- *_aidl_return = 0;
+ *_aidl_return = "FaceTrackerClient";
return ndk::ScopedAStatus::ok();
}
return ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED);
diff --git a/computepipe/router/1.0/PipeClient.cpp b/computepipe/router/1.0/PipeClient.cpp
index 817782a..b7e299d 100644
--- a/computepipe/router/1.0/PipeClient.cpp
+++ b/computepipe/router/1.0/PipeClient.cpp
@@ -33,14 +33,13 @@
mClientInfo(info) {
}
-uint32_t PipeClient::getClientId() {
+std::string PipeClient::getClientName() {
if (mClientInfo == nullptr) {
return 0;
}
- int id = 0;
- auto status = mClientInfo->getClientId(&id);
- uint32_t res = (status.isOk() && id > 0) ? id : 0;
- return res;
+ std::string name;
+ auto status = mClientInfo->getClientName(&name);
+ return (status.isOk()) ? name : "";
}
bool PipeClient::startClientMonitor() {
diff --git a/computepipe/router/1.0/include/PipeClient.h b/computepipe/router/1.0/include/PipeClient.h
index 03fc8f3..32730f4 100644
--- a/computepipe/router/1.0/include/PipeClient.h
+++ b/computepipe/router/1.0/include/PipeClient.h
@@ -44,7 +44,7 @@
explicit PipeClient(
const std::shared_ptr<aidl::android::automotive::computepipe::registry::IClientInfo>& info);
bool startClientMonitor() override;
- uint32_t getClientId() override;
+ std::string getClientName() override;
bool isAlive() override;
~PipeClient();
diff --git a/computepipe/router/include/ClientHandle.h b/computepipe/router/include/ClientHandle.h
index 7d026d2..4e2796a 100644
--- a/computepipe/router/include/ClientHandle.h
+++ b/computepipe/router/include/ClientHandle.h
@@ -18,6 +18,7 @@
#define ANDROID_AUTOMOTIVE_COMPUTEPIPE_ROUTER_CLIENT_HANDLE
#include <cstdint>
+#include <string>
namespace android {
namespace automotive {
@@ -34,7 +35,7 @@
class ClientHandle {
public:
/* Get client identifier as defined by the graph */
- virtual uint32_t getClientId() = 0;
+ virtual std::string getClientName() = 0;
/* Is client alive */
virtual bool isAlive() = 0;
/* start client monitor */
diff --git a/computepipe/runner/utils/DebuggerInterface.cc b/computepipe/runner/utils/DebuggerInterface.cc
deleted file mode 100644
index 66556da..0000000
--- a/computepipe/runner/utils/DebuggerInterface.cc
+++ /dev/null
@@ -1,13 +0,0 @@
-// 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.
diff --git a/computepipe/runner/utils/DebuggerInterface.h b/computepipe/runner/utils/DebuggerInterface.h
deleted file mode 100644
index d262ec3..0000000
--- a/computepipe/runner/utils/DebuggerInterface.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// 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.
-
-#ifndef COMPUTEPIPE_RUNNER_UTILS_DEBUGGERINTERFACE_H_
-#define COMPUTEPIPE_RUNNER_UTILS_DEBUGGERINTERFACE_H_
-
-#include <string>
-
-#include "DebuggerInterfaceCallbacks.h"
-
-namespace android {
-namespace automotive {
-namespace computepipe {
-namespace runner_utils {
-
-// DebuggerInterface registers IPipeDebugger interface with computepipe router.
-// DebuggerInterface handles binder IPC calls and invokes appropriate
-// callbacks.
-class DebuggerInterface {
- public:
- explicit DebuggerInterface(
- const std::string& graph_name,
- const DebuggerInterfaceCallbacks& debuggerInterfaceCallbacks);
-};
-
-} // namespace runner_utils
-} // namespace computepipe
-} // namespace automotive
-} // namespace android
-
-#endif // COMPUTEPIPE_RUNNER_UTILS_DEBUGGERINTERFACE_H_
diff --git a/computepipe/runner/utils/DebuggerInterfaceCallbacks.h b/computepipe/runner/utils/DebuggerInterfaceCallbacks.h
deleted file mode 100644
index 2671e48..0000000
--- a/computepipe/runner/utils/DebuggerInterfaceCallbacks.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// 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.
-
-#ifndef COMPUTEPIPE_RUNNER_UTILS_DEBUGGERINTERFACECALLBACKS_H_
-#define COMPUTEPIPE_RUNNER_UTILS_DEBUGGERINTERFACECALLBACKS_H_
-
-#include <functional>
-#include <string>
-
-namespace android {
-namespace automotive {
-namespace computepipe {
-namespace runner_utils {
-
-struct DebuggerInterfaceCallbacks {
- explicit DebuggerInterfaceCallbacks(
- std::function<Status()> startGraphProfile,
- std::function<Status()> stopGraphProfile,
- std::function<std::string()> GetDebugData) :
- mStartGraphProfile(startGraphProfile),
- mStopGraphProfile(stopGraphProfile),
- mGetDebugData(GetDebugData) {}
- const std::function<Status()> mStartGraphProfile;
- const std::function<Status()> mStopGraphProfile;
- const std::function<std::string()> mGetDebugData;
-};
-
-} // namespace runner_utils
-} // namespace computepipe
-} // namespace automotive
-} // namespace android
-
-#endif // COMPUTEPIPE_RUNNER_UTILS_DEBUGGERINTERFACECALLBACKS_H_
diff --git a/computepipe/runner/utils/RunnerInterfaceCallbacks.h b/computepipe/runner/utils/RunnerInterfaceCallbacks.h
deleted file mode 100644
index f2f8ece..0000000
--- a/computepipe/runner/utils/RunnerInterfaceCallbacks.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// 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.
-
-#ifndef COMPUTEPIPE_RUNNER_UTILS_RUNNERINTERFACECALLBACKS_H_
-#define COMPUTEPIPE_RUNNER_UTILS_RUNNERINTERFACECALLBACKS_H_
-
-#include <functional>
-#include <memory>
-#include <string>
-
-#include "ConfigurationCommand.pb.h"
-#include "ControlCommand.pb.h"
-#include "MemHandle.h"
-#include "types/Status.h"
-
-namespace android {
-namespace automotive {
-namespace computepipe {
-namespace runner_utils {
-
-struct RunnerInterfaceCallbacks {
- explicit RunnerInterfaceCallbacks(
- std::function<Status(const proto::ControlCommand&)> processControlCommand,
- std::function<Status(const proto::ConfigurationCommand&)> processConfigurationCommand,
- std::function<Status(const std::shared_ptr<MemHandle>&)> releasePacket)
- : mProcessControlCommand(processControlCommand),
- mProcessConfigurationCommand(processConfigurationCommand),
- mReleasePacket(releasePacket) {
- }
-
- const std::function<Status(const proto::ControlCommand&)> mProcessControlCommand;
- const std::function<Status(const proto::ConfigurationCommand&)>
- mProcessConfigurationCommand;
- const std::function<Status(const std::shared_ptr<MemHandle>&)> mReleasePacket;
-};
-
-} // namespace runner_utils
-} // namespace computepipe
-} // namespace automotive
-} // namespace android
-
-#endif // COMPUTEPIPE_RUNNER_UTILS_RUNNERINTERFACECALLBACKS_H_
diff --git a/computepipe/tests/PipeQueryTest.cpp b/computepipe/tests/PipeQueryTest.cpp
index acdee1d..3a03391 100644
--- a/computepipe/tests/PipeQueryTest.cpp
+++ b/computepipe/tests/PipeQueryTest.cpp
@@ -41,8 +41,8 @@
*/
class FakeClientInfo : public BnClientInfo {
public:
- ndk::ScopedAStatus getClientId(int32_t* id) override {
- *id = 1;
+ ndk::ScopedAStatus getClientName(std::string* name) override {
+ *name = "FakeClient";
return ndk::ScopedAStatus::ok();
}
};
diff --git a/computepipe/tests/runner/client_interface/ClientInterfaceTest.cc b/computepipe/tests/runner/client_interface/ClientInterfaceTest.cc
index fd35da5..619c23e 100644
--- a/computepipe/tests/runner/client_interface/ClientInterfaceTest.cc
+++ b/computepipe/tests/runner/client_interface/ClientInterfaceTest.cc
@@ -12,19 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <vector>
-
#include <aidl/android/automotive/computepipe/registry/BnClientInfo.h>
#include <aidl/android/automotive/computepipe/registry/IPipeQuery.h>
#include <aidl/android/automotive/computepipe/registry/IPipeRegistration.h>
#include <aidl/android/automotive/computepipe/runner/BnPipeStateCallback.h>
#include <aidl/android/automotive/computepipe/runner/BnPipeStream.h>
#include <aidl/android/automotive/computepipe/runner/PipeState.h>
-
#include <android/binder_manager.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+
#include <utility>
+#include <vector>
#include "ConfigurationCommand.pb.h"
#include "ControlCommand.pb.h"
@@ -53,16 +52,16 @@
using ::aidl::android::automotive::computepipe::runner::IPipeStateCallback;
using ::aidl::android::automotive::computepipe::runner::PacketDescriptor;
using ::aidl::android::automotive::computepipe::runner::PipeState;
-using ::android::automotive::computepipe::tests::MockMemHandle;
using ::android::automotive::computepipe::runner::tests::MockRunnerEvent;
+using ::android::automotive::computepipe::tests::MockMemHandle;
using ::ndk::ScopedAStatus;
using ::ndk::SharedRefBase;
-using ::testing::AtLeast;
+using ::testing::_;
using ::testing::AnyNumber;
+using ::testing::AtLeast;
using ::testing::DoAll;
using ::testing::Return;
using ::testing::SaveArg;
-using ::testing::_;
const char kRegistryInterfaceName[] = "router";
int testIx = 0;
@@ -89,9 +88,9 @@
class ClientInfo : public BnClientInfo {
public:
- ScopedAStatus getClientId(int32_t* _aidl_return) {
+ ScopedAStatus getClientName(std::string* _aidl_return) {
if (_aidl_return) {
- *_aidl_return = 0;
+ *_aidl_return = "ClientInfo";
return ScopedAStatus::ok();
}
return ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED);
@@ -200,8 +199,7 @@
status = mPipeRunner->setPipeTermination(3);
EXPECT_EQ(status.getExceptionCode(), EX_TRANSACTION_FAILED);
EXPECT_EQ(command.has_set_termination_option(), true);
- EXPECT_EQ(command.set_termination_option().termination_option_id(),
- 3);
+ EXPECT_EQ(command.set_termination_option().termination_option_id(), 3);
// Test that set output callback returns error status.
std::shared_ptr<StreamCallback> streamCb = ndk::SharedRefBase::make<StreamCallback>();
@@ -209,8 +207,7 @@
EXPECT_EQ(status.getExceptionCode(), EX_TRANSACTION_FAILED);
EXPECT_EQ(command.has_set_output_stream(), true);
EXPECT_EQ(command.set_output_stream().stream_id(), 0);
- EXPECT_EQ(command.set_output_stream().max_inflight_packets_count(),
- 10);
+ EXPECT_EQ(command.set_output_stream().max_inflight_packets_count(), 10);
// Release runner here. This should remove registry entry from router registry.
mAidlClient.reset();
@@ -309,7 +306,7 @@
EXPECT_TRUE(mPipeRunner->init(stateCallback).isOk());
// Test that config complete status is conveyed to client.
- std::map<int,int> m;
+ std::map<int, int> m;
ClientConfig config(0, 0, 0, m);
config.setPhaseState(TRANSITION_COMPLETE);
EXPECT_EQ(mAidlClient->handleConfigPhase(config), Status::SUCCESS);
@@ -346,7 +343,7 @@
EXPECT_TRUE(mPipeRunner->init(stateCallback).isOk());
// Test that error while applying config is conveyed to client.
- std::map<int,int> m;
+ std::map<int, int> m;
ClientConfig config(0, 0, 0, m);
config.setPhaseState(ABORTED);
EXPECT_EQ(mAidlClient->handleConfigPhase(config), Status::SUCCESS);
@@ -380,23 +377,21 @@
EXPECT_TRUE(mPipeRunner->setPipeOutputConfig(0, 10, streamCb).isOk());
EXPECT_EQ(command.has_set_output_stream(), true);
EXPECT_EQ(command.set_output_stream().stream_id(), 0);
- EXPECT_EQ(command.set_output_stream().max_inflight_packets_count(),
- 10);
+ EXPECT_EQ(command.set_output_stream().max_inflight_packets_count(), 10);
// Send a packet to client and verify the packet.
std::shared_ptr<MockMemHandle> packet = std::make_unique<MockMemHandle>();
uint64_t timestamp = 100;
const std::string testData = "Test String.";
- EXPECT_CALL(*packet, getType()).Times(AtLeast(1))
+ EXPECT_CALL(*packet, getType())
+ .Times(AtLeast(1))
.WillRepeatedly(Return(proto::PacketType::SEMANTIC_DATA));
- EXPECT_CALL(*packet, getTimeStamp()).Times(AtLeast(1))
- .WillRepeatedly(Return(timestamp));
- EXPECT_CALL(*packet, getSize()).Times(AtLeast(1))
- .WillRepeatedly(Return(testData.size()));
- EXPECT_CALL(*packet, getData()).Times(AtLeast(1))
- .WillRepeatedly(Return(testData.c_str()));
- EXPECT_EQ(mAidlClient->dispatchPacketToClient(
- 0, static_cast<std::shared_ptr<MemHandle>>(packet)), Status::SUCCESS);
+ EXPECT_CALL(*packet, getTimeStamp()).Times(AtLeast(1)).WillRepeatedly(Return(timestamp));
+ EXPECT_CALL(*packet, getSize()).Times(AtLeast(1)).WillRepeatedly(Return(testData.size()));
+ EXPECT_CALL(*packet, getData()).Times(AtLeast(1)).WillRepeatedly(Return(testData.c_str()));
+ EXPECT_EQ(
+ mAidlClient->dispatchPacketToClient(0, static_cast<std::shared_ptr<MemHandle>>(packet)),
+ Status::SUCCESS);
EXPECT_EQ(streamCb->data, packet->getData());
EXPECT_EQ(streamCb->timestamp, packet->getTimeStamp());
}
diff --git a/evs/support_library/StreamHandler.cpp b/evs/support_library/StreamHandler.cpp
index 320f98b..5b962cc 100644
--- a/evs/support_library/StreamHandler.cpp
+++ b/evs/support_library/StreamHandler.cpp
@@ -275,7 +275,6 @@
output.usage = input.usage;
output.stride = input.stride;
output.pixelSize = input.pixelSize;
- output.bufferId = input.bufferId;
// free the allocated output frame handle if it is not null
if (output.memHandle.getNativeHandle() != nullptr) {
@@ -287,6 +286,7 @@
return false;
}
}
+ output.bufferId = input.bufferId;
// Create a GraphicBuffer from the existing handle
sp<GraphicBuffer> inputBuffer = new GraphicBuffer(
diff --git a/service/res/values/config.xml b/service/res/values/config.xml
index c0ef110..c3a9262 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -305,7 +305,9 @@
-->
<string-array translatable="false" name="config_allowed_optional_car_features">
<item>com.android.car.user.CarUserNoticeService</item>
+ <item>diagnostic</item>
<item>storage_monitoring</item>
+ <item>vehicle_map_subscriber_service</item>
</string-array>
<!-- Configuration to enable passenger support.
diff --git a/service/src/com/android/car/CarFeatureController.java b/service/src/com/android/car/CarFeatureController.java
index fdaa1c5..1e8a5c5 100644
--- a/service/src/com/android/car/CarFeatureController.java
+++ b/service/src/com/android/car/CarFeatureController.java
@@ -61,35 +61,33 @@
Car.AUDIO_SERVICE,
Car.BLUETOOTH_SERVICE,
Car.CAR_BUGREPORT_SERVICE,
+ Car.CAR_CONFIGURATION_SERVICE,
Car.CAR_DRIVING_STATE_SERVICE,
Car.CAR_MEDIA_SERVICE,
Car.CAR_NAVIGATION_SERVICE,
Car.CAR_OCCUPANT_ZONE_SERVICE,
+ Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE,
Car.CAR_USER_SERVICE,
+ Car.CAR_UX_RESTRICTION_SERVICE,
Car.INFO_SERVICE,
Car.PACKAGE_SERVICE,
Car.POWER_SERVICE,
Car.PROJECTION_SERVICE,
Car.PROPERTY_SERVICE,
Car.TEST_SERVICE,
- // Deprecated, but still should be supported
- Car.SENSOR_SERVICE,
+ // All items below here are deprecated, but still should be supported
Car.CAR_INSTRUMENT_CLUSTER_SERVICE,
Car.CABIN_SERVICE,
Car.HVAC_SERVICE,
- Car.VENDOR_EXTENSION_SERVICE,
- // Candidate for Optional, but stay mandatory for now until final decision is made.
- Car.CAR_CONFIGURATION_SERVICE,
- Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE,
- Car.DIAGNOSTIC_SERVICE,
- Car.CAR_UX_RESTRICTION_SERVICE,
- // Marked as optional, but requires additional work
- Car.VMS_SUBSCRIBER_SERVICE
+ Car.SENSOR_SERVICE,
+ Car.VENDOR_EXTENSION_SERVICE
));
private static final HashSet<String> OPTIONAL_FEATURES = new HashSet<>(Arrays.asList(
+ CarFeatures.FEATURE_CAR_USER_NOTICE_SERVICE,
+ Car.DIAGNOSTIC_SERVICE,
Car.STORAGE_MONITORING_SERVICE,
- CarFeatures.FEATURE_CAR_USER_NOTICE_SERVICE
+ Car.VMS_SUBSCRIBER_SERVICE
));
private static final String FEATURE_CONFIG_FILE_NAME = "car_feature_config.txt";
diff --git a/service/src/com/android/car/CarUxRestrictionsManagerService.java b/service/src/com/android/car/CarUxRestrictionsManagerService.java
index edd74ba..b2c7615 100644
--- a/service/src/com/android/car/CarUxRestrictionsManagerService.java
+++ b/service/src/com/android/car/CarUxRestrictionsManagerService.java
@@ -635,7 +635,7 @@
writer.println("UX Restriction display info:");
for (int i = mActivityViewDisplayInfoMap.size() - 1; i >= 0; --i) {
DisplayInfo info = mActivityViewDisplayInfoMap.valueAt(i);
- writer.printf("Display%d: physicalDisplayId=%d, owner=%s",
+ writer.printf("Display%d: physicalDisplayId=%d, owner=%s\n",
mActivityViewDisplayInfoMap.keyAt(i), info.mPhysicalDisplayId, info.mOwner);
}
}
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index 89f14e1..bcdc640 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -204,16 +204,28 @@
mSystemStateControllerService = new SystemStateControllerService(
serviceContext, mCarAudioService, this);
mCarStatsService = new CarStatsService(serviceContext);
- mVmsBrokerService = new VmsBrokerService();
- mVmsClientManager = new VmsClientManager(
- // CarStatsService needs to be passed to the constructor due to HAL init order
- serviceContext, mCarStatsService, mCarUserService, mVmsBrokerService,
- mHal.getVmsHal());
- mVmsSubscriberService = new VmsSubscriberService(
- serviceContext, mVmsBrokerService, mVmsClientManager, mHal.getVmsHal());
- mVmsPublisherService = new VmsPublisherService(
- serviceContext, mCarStatsService, mVmsBrokerService, mVmsClientManager);
- mCarDiagnosticService = new CarDiagnosticService(serviceContext, mHal.getDiagnosticHal());
+ if (mFeatureController.isFeatureEnabled(Car.VMS_SUBSCRIBER_SERVICE)) {
+ mVmsBrokerService = new VmsBrokerService();
+ mVmsClientManager = new VmsClientManager(
+ // CarStatsService needs to be passed to the constructor due to HAL init order
+ serviceContext, mCarStatsService, mCarUserService, mVmsBrokerService,
+ mHal.getVmsHal());
+ mVmsSubscriberService = new VmsSubscriberService(
+ serviceContext, mVmsBrokerService, mVmsClientManager, mHal.getVmsHal());
+ mVmsPublisherService = new VmsPublisherService(
+ serviceContext, mCarStatsService, mVmsBrokerService, mVmsClientManager);
+ } else {
+ mVmsBrokerService = null;
+ mVmsClientManager = null;
+ mVmsSubscriberService = null;
+ mVmsPublisherService = null;
+ }
+ if (mFeatureController.isFeatureEnabled(Car.DIAGNOSTIC_SERVICE)) {
+ mCarDiagnosticService = new CarDiagnosticService(serviceContext,
+ mHal.getDiagnosticHal());
+ } else {
+ mCarDiagnosticService = null;
+ }
if (mFeatureController.isFeatureEnabled(Car.STORAGE_MONITORING_SERVICE)) {
mCarStorageMonitoringService = new CarStorageMonitoringService(serviceContext,
systemInterface);
@@ -265,12 +277,12 @@
allServices.add(mPerUserCarServiceHelper);
allServices.add(mCarBluetoothService);
allServices.add(mCarProjectionService);
- allServices.add(mCarDiagnosticService);
+ addServiceIfNonNull(allServices, mCarDiagnosticService);
addServiceIfNonNull(allServices, mCarStorageMonitoringService);
allServices.add(mCarConfigurationService);
- allServices.add(mVmsClientManager);
- allServices.add(mVmsSubscriberService);
- allServices.add(mVmsPublisherService);
+ addServiceIfNonNull(allServices, mVmsClientManager);
+ addServiceIfNonNull(allServices, mVmsSubscriberService);
+ addServiceIfNonNull(allServices, mVmsPublisherService);
allServices.add(mCarTrustedDeviceService);
allServices.add(mCarMediaService);
allServices.add(mCarLocationService);
diff --git a/service/src/com/android/car/audio/CarAudioService.java b/service/src/com/android/car/audio/CarAudioService.java
index 5466d80..8c47105 100644
--- a/service/src/com/android/car/audio/CarAudioService.java
+++ b/service/src/com/android/car/audio/CarAudioService.java
@@ -36,6 +36,7 @@
import android.hardware.automotive.audiocontrol.V1_0.IAudioControl;
import android.media.AudioAttributes;
import android.media.AudioAttributes.AttributeSystemUsage;
+import android.media.AudioDeviceAddress;
import android.media.AudioDeviceInfo;
import android.media.AudioDevicePort;
import android.media.AudioFocusInfo;
@@ -412,10 +413,16 @@
.collect(Collectors.toList());
}
+ private AudioDeviceInfo[] getAllInputDevices() {
+ return mAudioManager.getDevices(
+ AudioManager.GET_DEVICES_INPUTS);
+ }
+
private CarAudioZone[] loadCarAudioConfiguration(List<CarAudioDeviceInfo> carAudioDeviceInfos) {
+ AudioDeviceInfo[] inputDevices = getAllInputDevices();
try (InputStream inputStream = new FileInputStream(mCarAudioConfigurationPath)) {
CarAudioZonesHelper zonesHelper = new CarAudioZonesHelper(mContext, inputStream,
- carAudioDeviceInfos);
+ carAudioDeviceInfos, inputDevices);
return zonesHelper.loadAudioZones();
} catch (IOException | XmlPullParserException e) {
throw new RuntimeException("Failed to parse audio zone configuration", e);
@@ -1024,6 +1031,21 @@
}
/**
+ * Gets the input devices for zone zoneId
+ */
+ public @NonNull List<AudioDeviceAddress> getInputDevicesAddressesForZoneId(int zoneId) {
+ enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS);
+ Preconditions.checkArgumentInRange(zoneId, 0, mCarAudioZones.length - 1,
+ "zoneId out of range: " + zoneId);
+ for (CarAudioZone zone : mCarAudioZones) {
+ if (zone.getId() == zoneId) {
+ return zone.getInputAudioDeviceAddresses();
+ }
+ }
+ throw new IllegalArgumentException("zoneId does not exist" + zoneId);
+ }
+
+ /**
* Gets volume group by a given legacy stream type
* @param streamType Legacy stream type such as {@link AudioManager#STREAM_MUSIC}
* @return volume group id mapped from stream type
diff --git a/service/src/com/android/car/audio/CarAudioZone.java b/service/src/com/android/car/audio/CarAudioZone.java
index f512d48..63b0131 100644
--- a/service/src/com/android/car/audio/CarAudioZone.java
+++ b/service/src/com/android/car/audio/CarAudioZone.java
@@ -16,6 +16,7 @@
package com.android.car.audio;
import android.car.media.CarAudioManager;
+import android.media.AudioDeviceAddress;
import android.media.AudioDeviceInfo;
import android.util.Log;
import android.view.DisplayAddress;
@@ -45,12 +46,14 @@
private final String mName;
private final List<CarVolumeGroup> mVolumeGroups;
private final List<DisplayAddress.Physical> mPhysicalDisplayAddresses;
+ private List<AudioDeviceAddress> mInputAudioDeviceAddress;
CarAudioZone(int id, String name) {
mId = id;
mName = name;
mVolumeGroups = new ArrayList<>();
mPhysicalDisplayAddresses = new ArrayList<>();
+ mInputAudioDeviceAddress = new ArrayList<>();
}
int getId() {
@@ -169,15 +172,23 @@
}
void dump(String indent, PrintWriter writer) {
+ String internalIndent = indent + "\t";
writer.printf("%sCarAudioZone(%s:%d) isPrimary? %b\n", indent, mName, mId, isPrimaryZone());
for (DisplayAddress.Physical physical: mPhysicalDisplayAddresses) {
long port = (long) physical.getPort();
- writer.printf("%sDisplayAddress.Physical(%d)\n", indent + "\t", port);
+ writer.printf("%sDisplayAddress.Physical(%d)\n", internalIndent, port);
}
writer.println();
for (CarVolumeGroup group : mVolumeGroups) {
- group.dump(indent + "\t", writer);
+ group.dump(internalIndent, writer);
+ }
+
+ writer.printf("%sInput Audio Device Addresses\n", internalIndent);
+ String devicesIndent = internalIndent + "\t";
+ for (AudioDeviceAddress audioDeviceAddress : mInputAudioDeviceAddress) {
+ writer.printf("%sDevice Address(%s)\n", devicesIndent,
+ audioDeviceAddress.getAddress());
}
writer.println();
}
@@ -206,4 +217,12 @@
group.loadVolumesForUser(userId);
}
}
+
+ void addInputAudioDeviceAddress(AudioDeviceAddress address) {
+ mInputAudioDeviceAddress.add(address);
+ }
+
+ List<AudioDeviceAddress> getInputAudioDeviceAddresses() {
+ return mInputAudioDeviceAddress;
+ }
}
diff --git a/service/src/com/android/car/audio/CarAudioZonesHelper.java b/service/src/com/android/car/audio/CarAudioZonesHelper.java
index 640dd0a..dd1eadb 100644
--- a/service/src/com/android/car/audio/CarAudioZonesHelper.java
+++ b/service/src/com/android/car/audio/CarAudioZonesHelper.java
@@ -19,7 +19,10 @@
import android.car.media.CarAudioManager;
import android.content.Context;
import android.hardware.automotive.audiocontrol.V1_0.ContextNumber;
+import android.media.AudioDeviceAddress;
+import android.media.AudioDeviceInfo;
import android.text.TextUtils;
+import android.util.SparseIntArray;
import android.util.Xml;
import android.view.DisplayAddress;
@@ -36,6 +39,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@@ -43,6 +47,7 @@
* A helper class loads all audio zones from the configuration XML file.
*/
/* package */ class CarAudioZonesHelper {
+ private static final int INVALID_ZONE_ID = -1;
private static final String NAMESPACE = null;
private static final String TAG_ROOT = "carAudioConfiguration";
@@ -60,12 +65,20 @@
private static final String ATTR_DEVICE_ADDRESS = "address";
private static final String ATTR_CONTEXT_NAME = "context";
private static final String ATTR_PHYSICAL_PORT = "port";
- private static final int SUPPORTED_VERSION = 1;
+ private static final String ATTR_ZONE_ID = "audioZoneId";
+ private static final String ATTR_OCCUPANT_ZONE_ID = "occupantZoneId";
+ private static final String TAG_INPUT_DEVICES = "inputDevices";
+ private static final String TAG_INPUT_DEVICE = "inputDevice";
+ private static final int INVALID_VERSION = -1;
+ private static final int SUPPORTED_VERSION_1 = 1;
+ private static final int SUPPORTED_VERSION_2 = 2;
+ private static final SparseIntArray SUPPORTED_VERSIONS;
+
private static final Map<String, Integer> CONTEXT_NAME_MAP;
static {
- CONTEXT_NAME_MAP = new HashMap<>();
+ CONTEXT_NAME_MAP = new HashMap<>(8);
CONTEXT_NAME_MAP.put("music", ContextNumber.MUSIC);
CONTEXT_NAME_MAP.put("navigation", ContextNumber.NAVIGATION);
CONTEXT_NAME_MAP.put("voice_command", ContextNumber.VOICE_COMMAND);
@@ -74,27 +87,52 @@
CONTEXT_NAME_MAP.put("alarm", ContextNumber.ALARM);
CONTEXT_NAME_MAP.put("notification", ContextNumber.NOTIFICATION);
CONTEXT_NAME_MAP.put("system_sound", ContextNumber.SYSTEM_SOUND);
+
+ SUPPORTED_VERSIONS = new SparseIntArray(2);
+ SUPPORTED_VERSIONS.put(SUPPORTED_VERSION_1, SUPPORTED_VERSION_1);
+ SUPPORTED_VERSIONS.put(SUPPORTED_VERSION_2, SUPPORTED_VERSION_2);
}
private final Context mContext;
private final Map<String, CarAudioDeviceInfo> mAddressToCarAudioDeviceInfo;
+ private final Map<String, AudioDeviceInfo> mAddressToInputAudioDeviceInfo;
private final InputStream mInputStream;
private final Set<Long> mPortIds;
+ private final SparseIntArray mZoneIdToOccupantZoneIdMapping;
+ private final Set<Integer> mAudioZoneIds;
+ private final Set<String> mInputAudioDeviceAddresses;
private boolean mHasPrimaryZone;
private int mNextSecondaryZoneId;
+ private int mCurrentVersion;
+ /**
+ * <p><b>Note: <b/> CarAudioZonesHelper is expected to be used
+ * from a single thread. This should be the same thread that
+ * originally called new CarAudioZonesHelper.<p/>
+ *
+ */
CarAudioZonesHelper(Context context, @NonNull InputStream inputStream,
- @NonNull List<CarAudioDeviceInfo> carAudioDeviceInfos) {
+ @NonNull List<CarAudioDeviceInfo> carAudioDeviceInfos,
+ @NonNull AudioDeviceInfo[] inputDeviceInfo) {
mContext = context;
mInputStream = inputStream;
mAddressToCarAudioDeviceInfo = CarAudioZonesHelper.generateAddressToInfoMap(
carAudioDeviceInfos);
-
+ mAddressToInputAudioDeviceInfo =
+ CarAudioZonesHelper.generateAddressToInputAudioDeviceInfoMap(inputDeviceInfo);
mNextSecondaryZoneId = CarAudioManager.PRIMARY_AUDIO_ZONE + 1;
mPortIds = new HashSet<>();
+ mZoneIdToOccupantZoneIdMapping = new SparseIntArray();
+ mAudioZoneIds = new HashSet<>();
+ mInputAudioDeviceAddresses = new HashSet<>();
}
+ SparseIntArray getCarAudioZoneIdToOccupantZoneIdMapping() {
+ return mZoneIdToOccupantZoneIdMapping;
+ }
+
+ // TODO: refactor this method to return List<CarAudioZone>
CarAudioZone[] loadAudioZones() throws IOException, XmlPullParserException {
List<CarAudioZone> carAudioZones = new ArrayList<>();
parseCarAudioZones(carAudioZones, mInputStream);
@@ -108,6 +146,19 @@
.collect(Collectors.toMap(CarAudioDeviceInfo::getAddress, info -> info));
}
+ private static Map<String, AudioDeviceInfo> generateAddressToInputAudioDeviceInfoMap(
+ @NonNull AudioDeviceInfo[] inputAudioDeviceInfos) {
+ HashMap<String, AudioDeviceInfo> deviceAddressToInputDeviceMap =
+ new HashMap<>(inputAudioDeviceInfos.length);
+ for (int i = 0; i < inputAudioDeviceInfos.length; ++i) {
+ AudioDeviceInfo device = inputAudioDeviceInfos[i];
+ if (device.isSource()) {
+ deviceAddressToInputDeviceMap.put(device.getAddress(), device);
+ }
+ }
+ return deviceAddressToInputDeviceMap;
+ }
+
private void parseCarAudioZones(List<CarAudioZone> carAudioZones, InputStream stream)
throws XmlPullParserException, IOException {
final XmlPullParser parser = Xml.newPullParser();
@@ -121,11 +172,14 @@
// Version check
final int versionNumber = Integer.parseInt(
parser.getAttributeValue(NAMESPACE, ATTR_VERSION));
- if (versionNumber != SUPPORTED_VERSION) {
- throw new RuntimeException("Support version:"
- + SUPPORTED_VERSION + " only, got version:" + versionNumber);
+
+ if (SUPPORTED_VERSIONS.get(versionNumber, INVALID_VERSION) == INVALID_VERSION) {
+ throw new IllegalArgumentException("Latest Supported version:"
+ + SUPPORTED_VERSION_2 + " , got version:" + versionNumber);
}
+ mCurrentVersion = versionNumber;
+
// Get all zones configured under <zones> tag
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
@@ -160,10 +214,9 @@
mHasPrimaryZone = true;
}
final String zoneName = parser.getAttributeValue(NAMESPACE, ATTR_ZONE_NAME);
-
- CarAudioZone zone = new CarAudioZone(
- isPrimary ? CarAudioManager.PRIMARY_AUDIO_ZONE : getNextSecondaryZoneId(),
- zoneName);
+ final int audioZoneId = getZoneId(isPrimary, parser);
+ parseOccupantZoneId(audioZoneId, parser);
+ final CarAudioZone zone = new CarAudioZone(audioZoneId, zoneName);
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
// Expect one <volumeGroups> in one audio zone
@@ -171,6 +224,8 @@
parseVolumeGroups(parser, zone);
} else if (TAG_DISPLAYS.equals(parser.getName())) {
parseDisplays(parser, zone);
+ } else if (TAG_INPUT_DEVICES.equals(parser.getName())) {
+ parseInputAudioDevices(parser, zone);
} else {
skip(parser);
}
@@ -178,6 +233,100 @@
return zone;
}
+ private int getZoneId(boolean isPrimary, XmlPullParser parser) {
+ String audioZoneIdString = parser.getAttributeValue(NAMESPACE, ATTR_ZONE_ID);
+ if (mCurrentVersion == SUPPORTED_VERSION_1) {
+ Preconditions.checkArgument(audioZoneIdString == null,
+ "Invalid audio attribute %s"
+ + ", Please update car audio configurations file "
+ + "to version to 2 to use it.", ATTR_ZONE_ID);
+ return isPrimary ? CarAudioManager.PRIMARY_AUDIO_ZONE
+ : getNextSecondaryZoneId();
+ }
+ // Primary zone does not need to define it
+ if (isPrimary && audioZoneIdString == null) {
+ return CarAudioManager.PRIMARY_AUDIO_ZONE;
+ }
+ Objects.requireNonNull(audioZoneIdString, () ->
+ "Requires " + ATTR_ZONE_ID + " for all audio zones.");
+ int zoneId = parsePositiveIntAttribute(ATTR_ZONE_ID, audioZoneIdString);
+ //Verify that primary zone id is PRIMARY_AUDIO_ZONE
+ if (isPrimary) {
+ Preconditions.checkArgument(zoneId == CarAudioManager.PRIMARY_AUDIO_ZONE,
+ "Primary zone %s must be %d or it can be left empty.",
+ ATTR_ZONE_ID, CarAudioManager.PRIMARY_AUDIO_ZONE);
+ } else {
+ Preconditions.checkArgument(zoneId != CarAudioManager.PRIMARY_AUDIO_ZONE,
+ "%s can only be %d for primary zone.",
+ ATTR_ZONE_ID, CarAudioManager.PRIMARY_AUDIO_ZONE);
+ }
+ validateAudioZoneIdIsUnique(zoneId);
+ return zoneId;
+ }
+
+ private void parseOccupantZoneId(int audioZoneId, XmlPullParser parser) {
+ String occupantZoneIdString = parser.getAttributeValue(NAMESPACE, ATTR_OCCUPANT_ZONE_ID);
+ if (mCurrentVersion == SUPPORTED_VERSION_1) {
+ Preconditions.checkArgument(occupantZoneIdString == null,
+ "Invalid audio attribute %s"
+ + ", Please update car audio configurations file "
+ + "to version to 2 to use it.", ATTR_OCCUPANT_ZONE_ID);
+ return;
+ }
+ //Occupant id not required for all zones
+ if (occupantZoneIdString == null) {
+ return;
+ }
+ int occupantZoneId = parsePositiveIntAttribute(ATTR_OCCUPANT_ZONE_ID, occupantZoneIdString);
+ validateOccupantZoneIdIsUnique(occupantZoneId);
+ mZoneIdToOccupantZoneIdMapping.put(audioZoneId, occupantZoneId);
+ }
+
+ private int parsePositiveIntAttribute(String attribute, String integerString) {
+ try {
+ return Integer.parseUnsignedInt(integerString);
+ } catch (NumberFormatException | IndexOutOfBoundsException e) {
+ throw new IllegalArgumentException(attribute + " must be a positive integer, but was \""
+ + integerString + "\" instead.", e);
+ }
+ }
+ private void parseInputAudioDevices(XmlPullParser parser, CarAudioZone zone)
+ throws IOException, XmlPullParserException {
+ if (mCurrentVersion == SUPPORTED_VERSION_1) {
+ throw new IllegalStateException(
+ TAG_INPUT_DEVICES + " are not supported in car_audio_configuration.xml version "
+ + SUPPORTED_VERSION_1);
+ }
+ while (parser.next() != XmlPullParser.END_TAG) {
+ if (parser.getEventType() != XmlPullParser.START_TAG) continue;
+ if (TAG_INPUT_DEVICE.equals(parser.getName())) {
+ String audioDeviceAddress =
+ parser.getAttributeValue(NAMESPACE, ATTR_DEVICE_ADDRESS);
+ validateInputAudioDeviceAddress(audioDeviceAddress);
+ AudioDeviceInfo info = mAddressToInputAudioDeviceInfo.get(audioDeviceAddress);
+ Preconditions.checkArgument(info != null,
+ "%s %s of %s does not exist, add input device to"
+ + " audio_policy_configuration.xml.",
+ ATTR_DEVICE_ADDRESS, audioDeviceAddress, TAG_INPUT_DEVICE);
+ zone.addInputAudioDeviceAddress(new AudioDeviceAddress(info));
+ }
+ skip(parser);
+ }
+ }
+
+ private void validateInputAudioDeviceAddress(String audioDeviceAddress) {
+ Objects.requireNonNull(audioDeviceAddress, () ->
+ TAG_INPUT_DEVICE + " " + ATTR_DEVICE_ADDRESS + " attribute must be present.");
+ Preconditions.checkArgument(!audioDeviceAddress.isEmpty(),
+ "%s %s attribute can not be empty.",
+ TAG_INPUT_DEVICE, ATTR_DEVICE_ADDRESS);
+ if (mInputAudioDeviceAddresses.contains(audioDeviceAddress)) {
+ throw new IllegalArgumentException(TAG_INPUT_DEVICE + " " + audioDeviceAddress
+ + " repeats, " + TAG_INPUT_DEVICES + " can not repeat.");
+ }
+ mInputAudioDeviceAddresses.add(audioDeviceAddress);
+ }
+
private void parseDisplays(XmlPullParser parser, CarAudioZone zone)
throws IOException, XmlPullParserException {
while (parser.next() != XmlPullParser.END_TAG) {
@@ -208,6 +357,21 @@
mPortIds.add(portId);
}
+ private void validateOccupantZoneIdIsUnique(int occupantZoneId) {
+ if (mZoneIdToOccupantZoneIdMapping.indexOfValue(occupantZoneId) > -1) {
+ throw new IllegalArgumentException(ATTR_OCCUPANT_ZONE_ID + " " + occupantZoneId
+ + " is already associated with a zone");
+ }
+ }
+
+ private void validateAudioZoneIdIsUnique(int audioZoneId) {
+ if (mAudioZoneIds.contains(audioZoneId)) {
+ throw new IllegalArgumentException(ATTR_ZONE_ID + " " + audioZoneId
+ + " is already associated with a zone");
+ }
+ mAudioZoneIds.add(audioZoneId);
+ }
+
private void parseVolumeGroups(XmlPullParser parser, CarAudioZone zone)
throws XmlPullParserException, IOException {
int groupId = 0;
diff --git a/service/src/com/android/car/hal/DiagnosticHalService.java b/service/src/com/android/car/hal/DiagnosticHalService.java
index 506b922..f2027ab 100644
--- a/service/src/com/android/car/hal/DiagnosticHalService.java
+++ b/service/src/com/android/car/hal/DiagnosticHalService.java
@@ -49,7 +49,7 @@
*/
public class DiagnosticHalService extends HalServiceBase{
static final int OBD2_SELECTIVE_FRAME_CLEAR = 1;
- static final boolean DEBUG = true;
+ static final boolean DEBUG = false;
private final Object mLock = new Object();
private final VehicleHal mVehicleHal;
diff --git a/service/src/com/android/car/user/CarUserNoticeService.java b/service/src/com/android/car/user/CarUserNoticeService.java
index 70c5eb4..ba617c3 100644
--- a/service/src/com/android/car/user/CarUserNoticeService.java
+++ b/service/src/com/android/car/user/CarUserNoticeService.java
@@ -48,6 +48,8 @@
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
+import androidx.annotation.VisibleForTesting;
+
import com.android.car.CarLocalServices;
import com.android.car.CarServiceBase;
import com.android.car.R;
@@ -78,7 +80,7 @@
@Nullable
private final Intent mServiceIntent;
- private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+ private final Handler mMainHandler;
private final Object mLock = new Object();
@@ -203,6 +205,12 @@
};
public CarUserNoticeService(Context context) {
+ this(context, new Handler(Looper.getMainLooper()));
+ }
+
+ @VisibleForTesting
+ CarUserNoticeService(Context context, Handler handler) {
+ mMainHandler = handler;
Resources res = context.getResources();
String componentName = res.getString(R.string.config_userNoticeUiService);
if (componentName.isEmpty()) {
diff --git a/tests/CarDeveloperOptions/AndroidManifest.xml b/tests/CarDeveloperOptions/AndroidManifest.xml
index 7347c6a..436d7f0 100644
--- a/tests/CarDeveloperOptions/AndroidManifest.xml
+++ b/tests/CarDeveloperOptions/AndroidManifest.xml
@@ -85,7 +85,7 @@
<uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" />
<uses-permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_MODE" />
<uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
- <uses-permission android:name="android.permission.SET_TIME" />
+ <uses-permission android:name="android.permission.SUGGEST_MANUAL_TIME_AND_ZONE" />
<uses-permission android:name="android.permission.ACCESS_NOTIFICATIONS" />
<uses-permission android:name="android.permission.REBOOT" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/IccLockSettings.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/IccLockSettings.java
index 02bd469..0eff1c0 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/IccLockSettings.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/IccLockSettings.java
@@ -517,7 +517,7 @@
params.format = PixelFormat.TRANSLUCENT;
params.windowAnimations = com.android.internal.R.style.Animation_Toast;
params.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
- params.setFitWindowInsetsTypes(params.getFitWindowInsetsTypes() & ~Type.statusBars());
+ params.setFitInsetsTypes(params.getFitInsetsTypes() & ~Type.statusBars());
params.setTitle(errorMessage);
params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/DatePreferenceController.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/DatePreferenceController.java
index 2434f1b..99625d7 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/DatePreferenceController.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/DatePreferenceController.java
@@ -17,8 +17,9 @@
package com.android.car.developeroptions.datetime;
import android.app.Activity;
-import android.app.AlarmManager;
import android.app.DatePickerDialog;
+import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timedetector.TimeDetector;
import android.content.Context;
import android.text.TextUtils;
import android.text.format.DateFormat;
@@ -119,7 +120,10 @@
long when = Math.max(c.getTimeInMillis(), DatePreferenceHost.MIN_DATE);
if (when / 1000 < Integer.MAX_VALUE) {
- ((AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE)).setTime(when);
+ TimeDetector timeDetector = mContext.getSystemService(TimeDetector.class);
+ ManualTimeSuggestion manualTimeSuggestion =
+ TimeDetector.createManualTimeSuggestion(when, "Settings: Set date");
+ timeDetector.suggestManualTime(manualTimeSuggestion);
}
}
}
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/TimePreferenceController.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/TimePreferenceController.java
index aeabc6e..ef0e7a9 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/TimePreferenceController.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/TimePreferenceController.java
@@ -17,8 +17,9 @@
package com.android.car.developeroptions.datetime;
import android.app.Activity;
-import android.app.AlarmManager;
import android.app.TimePickerDialog;
+import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timedetector.TimeDetector;
import android.content.Context;
import android.text.TextUtils;
import android.text.format.DateFormat;
@@ -117,7 +118,10 @@
long when = Math.max(c.getTimeInMillis(), TimePreferenceHost.MIN_DATE);
if (when / 1000 < Integer.MAX_VALUE) {
- ((AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE)).setTime(when);
+ TimeDetector timeDetector = mContext.getSystemService(TimeDetector.class);
+ ManualTimeSuggestion manualTimeSuggestion =
+ TimeDetector.createManualTimeSuggestion(when, "Settings: Set time");
+ timeDetector.suggestManualTime(manualTimeSuggestion);
}
}
}
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/timezone/TimeZoneSettings.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/timezone/TimeZoneSettings.java
index 4f43cd3..481e6ba 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/timezone/TimeZoneSettings.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/timezone/TimeZoneSettings.java
@@ -17,8 +17,9 @@
package com.android.car.developeroptions.datetime.timezone;
import android.app.Activity;
-import android.app.AlarmManager;
import android.app.settings.SettingsEnums;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
+import android.app.timezonedetector.TimeZoneDetector;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -270,7 +271,10 @@
editor.putString(PREF_KEY_REGION, regionId);
}
editor.apply();
- getActivity().getSystemService(AlarmManager.class).setTimeZone(tzId);
+ TimeZoneDetector timeZoneDetector = getActivity().getSystemService(TimeZoneDetector.class);
+ ManualTimeZoneSuggestion suggestion = TimeZoneDetector.createManualTimeZoneSuggestion(
+ tzId, "Settings: Set time zone");
+ timeZoneDetector.suggestManualTimeZone(suggestion);
}
@Override
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/network/telephony/CellInfoUtil.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/network/telephony/CellInfoUtil.java
index 0be8ff6..5650d10 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/network/telephony/CellInfoUtil.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/network/telephony/CellInfoUtil.java
@@ -33,6 +33,7 @@
import com.android.internal.telephony.OperatorInfo;
+import java.util.Collections;
import java.util.List;
/**
@@ -149,7 +150,8 @@
mcc,
mnc,
operatorInfo.getOperatorAlphaLong(),
- operatorInfo.getOperatorAlphaShort());
+ operatorInfo.getOperatorAlphaShort(),
+ Collections.emptyList());
CellInfoGsm ci = new CellInfoGsm();
ci.setCellIdentity(cig);
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/sim/SimPreferenceDialog.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/sim/SimPreferenceDialog.java
index 39d3f2e..376f349 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/sim/SimPreferenceDialog.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/sim/SimPreferenceDialog.java
@@ -65,7 +65,7 @@
mSlotId = extras.getInt(SimSettings.EXTRA_SLOT_ID, -1);
mSubscriptionManager = SubscriptionManager.from(mContext);
mSubInfoRecord = mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(mSlotId);
- mTintArr = mContext.getResources().getIntArray(com.android.internal.R.array.sim_colors);
+ mTintArr = mContext.getResources().getIntArray(android.R.array.simColors);
mColorStrings = mContext.getResources().getStringArray(R.array.color_picker);
mTintSelectorPos = 0;
diff --git a/tests/DefaultStorageMonitoringCompanionApp/src/com/google/android/car/defaultstoragemonitoringcompanionapp/MainActivity.java b/tests/DefaultStorageMonitoringCompanionApp/src/com/google/android/car/defaultstoragemonitoringcompanionapp/MainActivity.java
index 159260a..02adca2 100644
--- a/tests/DefaultStorageMonitoringCompanionApp/src/com/google/android/car/defaultstoragemonitoringcompanionapp/MainActivity.java
+++ b/tests/DefaultStorageMonitoringCompanionApp/src/com/google/android/car/defaultstoragemonitoringcompanionapp/MainActivity.java
@@ -111,7 +111,11 @@
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "Connected to " + name.flattenToString());
-
+ if (!mCar.isFeatureEnabled(Car.STORAGE_MONITORING_SERVICE)) {
+ Log.e(TAG, "Car.STORAGE_MONITORING_SERVICE feature not supported, will finish");
+ finish();
+ return;
+ }
CarStorageMonitoringManager storageMonitoringManager =
(CarStorageMonitoringManager) mCar.getCarManager(
Car.STORAGE_MONITORING_SERVICE);
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/activity_view_test_fragment.xml b/tests/EmbeddedKitchenSinkApp/res/layout/activity_view_test_fragment.xml
index bd74423..568bad5 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/activity_view_test_fragment.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/activity_view_test_fragment.xml
@@ -19,7 +19,7 @@
<RelativeLayout android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="20dp">
- <ActivityView android:id="@+id/av_activityview"
+ <android.car.app.CarActivityView android:id="@+id/av_activityview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/package_info_test.xml b/tests/EmbeddedKitchenSinkApp/res/layout/package_info_test.xml
new file mode 100644
index 0000000..23a5765
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/package_info_test.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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
+ android:layout_width="fill_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <LinearLayout
+ android:id="@+id/header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/header_margin"
+ android:orientation="horizontal">
+ <Spinner
+ android:id = "@+id/spinner"
+ android:layout_width = "wrap_content"
+ android:layout_height = "wrap_content"
+ android:layout_weight = "1"
+ android:paddingLeft="@dimen/header_padding"
+ android:prompt = "@string/select_user"
+ android:textSize = "@dimen/spinner_text_size"
+ />
+ <CheckBox
+ android:id = "@+id/checkbox_activities"
+ android:layout_width = "wrap_content"
+ android:layout_height = "wrap_content"
+ android:layout_weight = "1"
+ android:paddingLeft="@dimen/header_padding"
+ android:text = "@string/checkbox_activities"
+ android:textSize = "@dimen/checkbox_text_size"
+ />
+ <CheckBox
+ android:id = "@+id/checkbox_services"
+ android:layout_width = "wrap_content"
+ android:layout_height = "wrap_content"
+ android:layout_weight = "1"
+ android:text = "@string/checkbox_services"
+ android:textSize = "@dimen/checkbox_text_size"
+ />
+ <CheckBox
+ android:id = "@+id/checkbox_permissions"
+ android:layout_width = "wrap_content"
+ android:layout_height = "wrap_content"
+ android:layout_weight = "1"
+ android:text = "@string/checkbox_permissions"
+ android:textSize = "@dimen/checkbox_text_size"
+ />
+ <CheckBox
+ android:id = "@+id/checkbox_shareduid"
+ android:layout_width = "wrap_content"
+ android:layout_height = "wrap_content"
+ android:layout_weight = "1"
+ android:text = "@string/checkbox_shareduid"
+ android:textSize = "@dimen/checkbox_text_size"
+ />
+ <Button
+ android:id = "@+id/button_show"
+ android:layout_width = "wrap_content"
+ android:layout_height = "wrap_content"
+ android:layout_weight = "1"
+ android:text = "@string/button_show"
+ android:textSize = "@dimen/checkbox_text_size"
+ />
+ </LinearLayout>
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/packages"/>
+ </ScrollView>
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml b/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml
index 437d69d..a649f9c 100644
--- a/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml
@@ -45,6 +45,12 @@
<dimen name="car_card_header_height">96dp</dimen>
<dimen name="car_card_action_bar_height">96dp</dimen>
+ <!-- Package info -->
+ <dimen name="spinner_text_size">16sp</dimen>
+ <dimen name="checkbox_text_size">16sp</dimen>
+ <dimen name="header_margin">8dp</dimen>
+ <dimen name="header_padding">8dp</dimen>
+
<!-- Paddings -->
<dimen name="car_padding_1">4dp</dimen>
<dimen name="car_padding_2">10dp</dimen>
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
index dce047f..da2d341 100644
--- a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
@@ -178,6 +178,14 @@
<string name="sw_center" translatable="false">Center</string>
<string name="sw_back" translatable="false">Back</string>
+ <!-- package info test -->
+ <string name="select_user" translatable="false">Select user</string>
+ <string name="checkbox_activities" translatable="false">contains only activities</string>
+ <string name="checkbox_services" translatable="false">services are not exported</string>
+ <string name="checkbox_permissions" translatable="false"> does not require selected permissions</string>
+ <string name="checkbox_shareduid" translatable="false">sharedUserId is not system</string>
+ <string name="button_show" translatable="false">Show</string>
+
<!-- power test -->
<string name="power_request_shutdown" translatable="false">Request Shutdown</string>
<string name="power_shutdown" translatable="false">Shutdown</string>
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 cb10104..d751a43 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
@@ -57,6 +57,7 @@
import com.google.android.car.kitchensink.hvac.HvacTestFragment;
import com.google.android.car.kitchensink.notification.NotificationFragment;
import com.google.android.car.kitchensink.orientation.OrientationTestFragment;
+import com.google.android.car.kitchensink.packageinfo.PackageInfoFragment;
import com.google.android.car.kitchensink.power.PowerTestFragment;
import com.google.android.car.kitchensink.projection.ProjectionFragment;
import com.google.android.car.kitchensink.property.PropertyTestFragment;
@@ -189,7 +190,8 @@
new FragmentMenuEntry("volume test", VolumeTestFragment.class),
new FragmentMenuEntry("vehicle ctrl", VehicleCtrlFragment.class),
new FragmentMenuEntry("vehicle hal", VehicleHalFragment.class),
- new FragmentMenuEntry("web links", WebLinksTestFragment.class)
+ new FragmentMenuEntry("web links", WebLinksTestFragment.class),
+ new FragmentMenuEntry("package info", PackageInfoFragment.class)
);
private Car mCarApi;
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/activityview/ActivityViewTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/activityview/ActivityViewTestFragment.java
index 1564061..40c470d 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/activityview/ActivityViewTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/activityview/ActivityViewTestFragment.java
@@ -16,7 +16,7 @@
package com.google.android.car.kitchensink.activityview;
-import android.app.ActivityView;
+import android.car.app.CarActivityView;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
@@ -36,7 +36,7 @@
* This fragment exercises the capabilities of virtual display.
*/
public class ActivityViewTestFragment extends Fragment {
- private ActivityView mActivityView;
+ private CarActivityView mActivityView;
private ViewGroup mActivityViewParent;
@Override
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/diagnostic/DiagnosticTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/diagnostic/DiagnosticTestFragment.java
index 177d36c..99164df 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/diagnostic/DiagnosticTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/diagnostic/DiagnosticTestFragment.java
@@ -92,8 +92,15 @@
}
private void resumeDiagnosticManager() {
+ Car car = mActivity.getCar();
+ if (!car.isFeatureEnabled(Car.DIAGNOSTIC_SERVICE)) {
+ String notSupported = Car.DIAGNOSTIC_SERVICE + " not supported";
+ mLiveDiagnosticInfo.setText(notSupported);
+ mFreezeDiagnosticInfo.setText(notSupported);
+ return;
+ }
mDiagnosticManager =
- (CarDiagnosticManager) mActivity.getCar().getCarManager(Car.DIAGNOSTIC_SERVICE);
+ (CarDiagnosticManager) car.getCarManager(Car.DIAGNOSTIC_SERVICE);
if (mLiveListener != null) {
mDiagnosticManager.registerListener(mLiveListener,
CarDiagnosticManager.FRAME_TYPE_LIVE,
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/packageinfo/PackageInfoFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/packageinfo/PackageInfoFragment.java
new file mode 100644
index 0000000..a43c510
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/packageinfo/PackageInfoFragment.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2020 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.packageinfo;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import androidx.fragment.app.Fragment;
+
+import com.google.android.car.kitchensink.R;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Test fragment to check packages installed for each user
+ * <p>
+ * Options to apply conditions to filter out packages, if package
+ * <ul>
+ * <li>only have activities.
+ * <li>have service but not exported or for single user.
+ * <li>does not require any key permission
+ * (INTERACT_ACROSS_USERS, INTERACT_ACROSS_USERS_FULL, WRITE_DEVICE_CONFIG).
+ * <li>does not have sharedUserId or shardUserId is not system uid.
+ * </ul>
+ */
+public final class PackageInfoFragment extends Fragment{
+ private static final String TAG = "PackageInfoTest";
+ private static final boolean DEBUG = true;
+ private static final int PACKAGE_FLAGS = PackageManager.GET_META_DATA
+ | PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES
+ | PackageManager.GET_PERMISSIONS | PackageManager.GET_SIGNATURES;
+ private static final List<String> IMPORTANT_PERMISSIONS = Arrays.asList(
+ "android.permission.INTERACT_ACROSS_USERS",
+ "android.permission.INTERACT_ACROSS_USERS_FULL",
+ "android.permission.WRITE_DEVICE_CONFIG");
+ private static final String SYSTEM_UID = "android.uid.system";
+
+ private final List<PackageInfo> mPackagesToDisableForSystemUser = new ArrayList<>();
+
+ private UserManager mUserManager;
+ private PackageManager mPackageManager;
+ private UserInfo mUserToShow;
+ private int mShowFlags;
+ private TextView mPackageView;
+ private boolean mFilterActivities;
+ private boolean mFilterServices;
+ private boolean mFilterPermissions;
+ private boolean mFilterSharedUid;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
+ mPackageManager = getActivity().getPackageManager();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) {
+ View view = inflater.inflate(R.layout.package_info_test, container, false);
+ setListener(view);
+
+ return view;
+ }
+
+ private void refreshPackages() {
+ List<PackageInfo> packages = new ArrayList<PackageInfo>();
+ try {
+ packages = mPackageManager.getInstalledPackagesAsUser(PackageManager.GET_META_DATA
+ | PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES
+ | PackageManager.GET_PERMISSIONS | PackageManager.GET_SIGNATURES,
+ mUserToShow.id);
+ if (DEBUG) {
+ Log.d(TAG, "total packages found: " + packages.size());
+ }
+ } catch (Exception e) {
+ if (DEBUG) {
+ Log.e(TAG, "failed to get packages for given user: " + mUserToShow);
+ }
+ mPackageView.setText("Cannot retrieve packages for this user..");
+ return;
+ }
+
+ mPackagesToDisableForSystemUser.clear();
+
+ for (PackageInfo packageInfo : packages) {
+ Log.d(TAG, "checking package: " + packageInfo);
+ boolean toBlacklist = true;
+ // check share user id, show package does not have sharedUserId or not system uid
+ if (mFilterSharedUid) {
+ if (DEBUG) {
+ Log.d(TAG, "sharedUid flagged: " + (packageInfo.sharedUserId == null
+ || !packageInfo.sharedUserId.equals(SYSTEM_UID)));
+ }
+
+ toBlacklist &= (packageInfo.sharedUserId == null
+ || !packageInfo.sharedUserId.equals(SYSTEM_UID));
+ }
+
+ // check permissions, show package does not require selected permissions
+ if (mFilterPermissions && packageInfo.requestedPermissions != null) {
+ if (DEBUG) {
+ for (String info : Arrays.asList(packageInfo.requestedPermissions)) {
+ Log.d(TAG, info + " flagged: " + (!IMPORTANT_PERMISSIONS.contains(info)));
+ }
+ }
+
+ toBlacklist &= !(Arrays.asList(packageInfo.requestedPermissions).stream().anyMatch(
+ info -> IMPORTANT_PERMISSIONS.contains(info)));
+ }
+ // check services, w/o service or service not exported and w/o single user flag
+ if (mFilterServices && packageInfo.services != null) {
+ if (DEBUG) {
+ for (ServiceInfo info : Arrays.asList(packageInfo.services)) {
+ Log.d(TAG, info + " flagged: " + (!info.exported
+ && (info.flags & ServiceInfo.FLAG_SINGLE_USER) == 0));
+ }
+ }
+
+ toBlacklist &= Arrays.asList(packageInfo.services).stream().anyMatch(info ->
+ !info.exported && (info.flags & ServiceInfo.FLAG_SINGLE_USER) == 0);
+ }
+ // check activities
+ if (mFilterActivities) {
+ if (DEBUG) {
+ Log.d(TAG, packageInfo + " contain activities only, flagged: " + (
+ packageInfo.activities != null
+ && packageInfo.services == null
+ && packageInfo.providers == null));
+ }
+ toBlacklist &= (packageInfo.activities != null
+ && packageInfo.services == null && packageInfo.providers == null);
+ }
+
+ if (toBlacklist) {
+ mPackagesToDisableForSystemUser.add(packageInfo);
+ }
+ }
+ }
+
+ private void showPackagesOnView(TextView tv) {
+ refreshPackages();
+
+ tv.setText(mPackagesToDisableForSystemUser.size() + " Packages found ...\n");
+
+ for (PackageInfo info : mPackagesToDisableForSystemUser) {
+ tv.append(info.packageName.toString() + "\n");
+ }
+ }
+
+ private void setListener(View v) {
+ List<UserInfo> users = mUserManager.getUsers();
+ mUserToShow = users.get(0);
+ Spinner spinner = v.findViewById(R.id.spinner);
+ ArrayAdapter<UserInfo> userArrayAdapter = new ArrayAdapter<UserInfo>(
+ getContext(), android.R.layout.simple_spinner_item, users);
+ userArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ spinner.setAdapter(userArrayAdapter);
+ spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View currentView,
+ int position, long id) {
+ mUserToShow = (UserInfo) parent.getItemAtPosition(position);
+ }
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ });
+
+ CheckBox activities = v.findViewById(R.id.checkbox_activities);
+ CheckBox services = v.findViewById(R.id.checkbox_services);
+ CheckBox permissions = v.findViewById(R.id.checkbox_permissions);
+ CheckBox shareduid = v.findViewById(R.id.checkbox_shareduid);
+ Button showButton = v.findViewById(R.id.button_show);
+ TextView packageView = v.findViewById(R.id.packages);
+
+ activities.setOnClickListener(view -> mFilterActivities = ((CheckBox) view).isChecked());
+ services.setOnClickListener(view -> mFilterServices = ((CheckBox) view).isChecked());
+ permissions.setOnClickListener(view -> mFilterPermissions = ((CheckBox) view).isChecked());
+ shareduid.setOnClickListener(view -> mFilterSharedUid = ((CheckBox) view).isChecked());
+ showButton.setOnClickListener(view -> showPackagesOnView(packageView));
+ }
+}
diff --git a/tests/SecurityPermissionTest/src/com/android/car/CarPublicTest.java b/tests/SecurityPermissionTest/src/com/android/car/CarPublicTest.java
index 2284694..af1867b 100644
--- a/tests/SecurityPermissionTest/src/com/android/car/CarPublicTest.java
+++ b/tests/SecurityPermissionTest/src/com/android/car/CarPublicTest.java
@@ -55,11 +55,18 @@
assertThrows(SecurityException.class, () -> mCar.getCarManager(Car.CAR_NAVIGATION_SERVICE));
assertThrows(SecurityException.class, () -> mCar.getCarManager(
Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE));
- assertThrows(SecurityException.class, () -> mCar.getCarManager(Car.DIAGNOSTIC_SERVICE));
+ if (mCar.isFeatureEnabled(Car.DIAGNOSTIC_SERVICE)) {
+ assertThrows(SecurityException.class, () -> mCar.getCarManager(Car.DIAGNOSTIC_SERVICE));
+ }
assertThrows(SecurityException.class, () -> mCar.getCarManager(Car.POWER_SERVICE));
- assertThrows(SecurityException.class, () -> mCar.getCarManager(Car.VMS_SUBSCRIBER_SERVICE));
+ if (mCar.isFeatureEnabled(Car.VMS_SUBSCRIBER_SERVICE)) {
+ assertThrows(SecurityException.class, () -> mCar.getCarManager(
+ Car.VMS_SUBSCRIBER_SERVICE));
+ }
assertThrows(SecurityException.class, () -> mCar.getCarManager(Car.TEST_SERVICE));
- assertThrows(SecurityException.class, () -> mCar.getCarManager(
- Car.STORAGE_MONITORING_SERVICE));
+ if (mCar.isFeatureEnabled(Car.STORAGE_MONITORING_SERVICE)) {
+ assertThrows(SecurityException.class, () -> mCar.getCarManager(
+ Car.STORAGE_MONITORING_SERVICE));
+ }
}
}
diff --git a/tests/VmsSubscriberClientSample/src/com/google/android/car/vms/subscriber/VmsSubscriberClientSampleActivity.java b/tests/VmsSubscriberClientSample/src/com/google/android/car/vms/subscriber/VmsSubscriberClientSampleActivity.java
index 3730104..3faa93f 100644
--- a/tests/VmsSubscriberClientSample/src/com/google/android/car/vms/subscriber/VmsSubscriberClientSampleActivity.java
+++ b/tests/VmsSubscriberClientSample/src/com/google/android/car/vms/subscriber/VmsSubscriberClientSampleActivity.java
@@ -80,6 +80,10 @@
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "Connected to Car Service");
+ if (!mCarApi.isFeatureEnabled(Car.VMS_SUBSCRIBER_SERVICE)) {
+ Log.e(TAG, "VMS not supported");
+ finish();
+ }
mVmsSubscriberManager = getVmsSubscriberManager();
configureSubscriptions(mVmsSubscriberManager);
}
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarDiagnosticManagerTest.java b/tests/android_car_api_test/src/android/car/apitest/CarDiagnosticManagerTest.java
index 301ad87..12030bb 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarDiagnosticManagerTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarDiagnosticManagerTest.java
@@ -16,6 +16,8 @@
package android.car.apitest;
+import static org.junit.Assume.assumeTrue;
+
import android.car.Car;
import android.car.diagnostic.CarDiagnosticEvent;
import android.car.diagnostic.CarDiagnosticManager;
@@ -78,6 +80,7 @@
InstrumentationRegistry.getInstrumentation().getContext(), mConnectionListener);
mCar.connect();
waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
+ assumeTrue(mCar.isFeatureEnabled(Car.DIAGNOSTIC_SERVICE));
mCarDiagnosticManager = (CarDiagnosticManager) mCar.getCarManager(Car.DIAGNOSTIC_SERVICE);
assertNotNull(mCarDiagnosticManager);
}
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarFeatureTest.java b/tests/android_car_api_test/src/android/car/apitest/CarFeatureTest.java
index 73c300b..46619bb 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarFeatureTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarFeatureTest.java
@@ -39,35 +39,33 @@
Car.AUDIO_SERVICE,
Car.BLUETOOTH_SERVICE,
Car.CAR_BUGREPORT_SERVICE,
+ Car.CAR_CONFIGURATION_SERVICE,
Car.CAR_DRIVING_STATE_SERVICE,
Car.CAR_MEDIA_SERVICE,
Car.CAR_NAVIGATION_SERVICE,
Car.CAR_OCCUPANT_ZONE_SERVICE,
+ Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE,
Car.CAR_USER_SERVICE,
+ Car.CAR_UX_RESTRICTION_SERVICE,
Car.INFO_SERVICE,
Car.PACKAGE_SERVICE,
Car.POWER_SERVICE,
Car.PROJECTION_SERVICE,
Car.PROPERTY_SERVICE,
Car.TEST_SERVICE,
- // Deprecated, but still should be supported
- Car.SENSOR_SERVICE,
+ // All items below here are deprecated, but still should be supported
Car.CAR_INSTRUMENT_CLUSTER_SERVICE,
Car.CABIN_SERVICE,
Car.HVAC_SERVICE,
- Car.VENDOR_EXTENSION_SERVICE,
- // Candidate for Optional, but stay mandatory for now until final decision is made.
- Car.CAR_CONFIGURATION_SERVICE,
- Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE,
- Car.DIAGNOSTIC_SERVICE,
- Car.CAR_UX_RESTRICTION_SERVICE,
- // Marked as optional, but requires additional work
- Car.VMS_SUBSCRIBER_SERVICE
+ Car.SENSOR_SERVICE,
+ Car.VENDOR_EXTENSION_SERVICE
);
private static final List<String> OPTIONAL_FEATURES = Arrays.asList(
+ CarFeatures.FEATURE_CAR_USER_NOTICE_SERVICE,
+ Car.DIAGNOSTIC_SERVICE,
Car.STORAGE_MONITORING_SERVICE,
- CarFeatures.FEATURE_CAR_USER_NOTICE_SERVICE
+ Car.VMS_SUBSCRIBER_SERVICE
);
private static final String NON_EXISTING_FEATURE = "ThisFeatureDoesNotExist";
diff --git a/tests/carservice_test/res/raw/car_audio_configuration.xml b/tests/carservice_test/res/raw/car_audio_configuration.xml
index 4ae5217..2d7c609 100644
--- a/tests/carservice_test/res/raw/car_audio_configuration.xml
+++ b/tests/carservice_test/res/raw/car_audio_configuration.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
-<carAudioConfiguration version="1">
+<carAudioConfiguration version="2">
<zones>
- <zone name="primary zone" isPrimary="true">
+ <zone name="primary zone" isPrimary="true" audioZoneId="0" occupantZoneId="1">
<volumeGroups>
<group>
<device address="bus0_media_out">
@@ -22,7 +22,7 @@
<display port="2"/>
</displays>
</zone>
- <zone name="rear seat zone">
+ <zone name="rear seat zone" audioZoneId="2">
<volumeGroups>
<group>
<device address="bus100_rear_seat">
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_V1.xml b/tests/carservice_test/res/raw/car_audio_configuration_V1.xml
new file mode 100644
index 0000000..4aaaa6c
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_V1.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<carAudioConfiguration version="1">
+ <zones>
+ <zone name="primary zone" isPrimary="true">
+ <volumeGroups>
+ <group>
+ <device address="bus0_media_out">
+ <context context="music"/>
+ </device>
+ <device address="bus3_call_ring_out">
+ <context context="call_ring"/>
+ </device>
+ </group>
+ <group>
+ <device address="bus1_navigation_out">
+ <context context="navigation"/>
+ </device>
+ </group>
+ </volumeGroups>
+ <displays>
+ <display port="1"/>
+ <display port="2"/>
+ </displays>
+ </zone>
+ <zone name="rear seat zone">
+ <volumeGroups>
+ <group>
+ <device address="bus100_rear_seat">
+ <context context="music"/>
+ <context context="navigation"/>
+ <context context="voice_command"/>
+ <context context="call_ring"/>
+ <context context="call"/>
+ <context context="alarm"/>
+ <context context="notification"/>
+ <context context="system_sound"/>
+ </device>
+ </group>
+ </volumeGroups>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_V1_with_audio_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_V1_with_audio_zone_id.xml
new file mode 100644
index 0000000..5793ab7
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_V1_with_audio_zone_id.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<carAudioConfiguration version="1">
+ <zones>
+ <zone name="primary zone" isPrimary="true" audioZoneId="0">
+ <volumeGroups>
+ <group>
+ <device address="bus0_media_out">
+ <context context="music"/>
+ </device>
+ <device address="bus3_call_ring_out">
+ <context context="call_ring"/>
+ </device>
+ </group>
+ <group>
+ <device address="bus1_navigation_out">
+ <context context="navigation"/>
+ </device>
+ </group>
+ </volumeGroups>
+ </zone>
+ <zone name="rear seat zone">
+ <volumeGroups>
+ <group>
+ <device address="bus100_rear_seat">
+ <context context="music"/>
+ <context context="navigation"/>
+ <context context="voice_command"/>
+ <context context="call_ring"/>
+ <context context="call"/>
+ <context context="alarm"/>
+ <context context="notification"/>
+ <context context="system_sound"/>
+ </device>
+ </group>
+ </volumeGroups>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_V1_with_occupant_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_V1_with_occupant_zone_id.xml
new file mode 100644
index 0000000..564a13c
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_V1_with_occupant_zone_id.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<carAudioConfiguration version="1">
+ <zones>
+ <zone name="primary zone" isPrimary="true" occupantZoneId="1">
+ <volumeGroups>
+ <group>
+ <device address="bus0_media_out">
+ <context context="music"/>
+ </device>
+ <device address="bus3_call_ring_out">
+ <context context="call_ring"/>
+ </device>
+ </group>
+ <group>
+ <device address="bus1_navigation_out">
+ <context context="navigation"/>
+ </device>
+ </group>
+ </volumeGroups>
+ </zone>
+ <zone name="rear seat zone">
+ <volumeGroups>
+ <group>
+ <device address="bus100_rear_seat">
+ <context context="music"/>
+ <context context="navigation"/>
+ <context context="voice_command"/>
+ <context context="call_ring"/>
+ <context context="call"/>
+ <context context="alarm"/>
+ <context context="notification"/>
+ <context context="system_sound"/>
+ </device>
+ </group>
+ </volumeGroups>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_duplicate_audio_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_duplicate_audio_zone_id.xml
new file mode 100644
index 0000000..2743c41
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_duplicate_audio_zone_id.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<carAudioConfiguration version="2">
+ <zones>
+ <zone name="primary zone" isPrimary="true" audioZoneId="0" occupantZoneId="1">
+ <volumeGroups>
+ <group>
+ <device address="bus0_media_out">
+ <context context="music"/>
+ </device>
+ <device address="bus3_call_ring_out">
+ <context context="call_ring"/>
+ </device>
+ </group>
+ <group>
+ <device address="bus1_navigation_out">
+ <context context="navigation"/>
+ </device>
+ </group>
+ </volumeGroups>
+ <displays>
+ <display port="1"/>
+ <display port="2"/>
+ </displays>
+ </zone>
+ <zone name="rear seat zone" audioZoneId="1" occupantZoneId="2">
+ <volumeGroups>
+ <group>
+ <device address="bus100_rear_seat">
+ <context context="music"/>
+ <context context="navigation"/>
+ <context context="voice_command"/>
+ <context context="call_ring"/>
+ <context context="call"/>
+ <context context="alarm"/>
+ <context context="notification"/>
+ <context context="system_sound"/>
+ </device>
+ </group>
+ </volumeGroups>
+ </zone>
+ <zone name="rear seat zone 2" audioZoneId="1" occupantZoneId="3">
+ <volumeGroups>
+ <group>
+ <device address="bus200_rear_seat">
+ <context context="music"/>
+ <context context="navigation"/>
+ <context context="voice_command"/>
+ <context context="call_ring"/>
+ <context context="call"/>
+ <context context="alarm"/>
+ <context context="notification"/>
+ <context context="system_sound"/>
+ </device>
+ </group>
+ </volumeGroups>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_duplicate_occupant_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_duplicate_occupant_zone_id.xml
new file mode 100644
index 0000000..613f6cd
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_duplicate_occupant_zone_id.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<carAudioConfiguration version="2">
+ <zones>
+ <zone name="primary zone" isPrimary="true" audioZoneId="0" occupantZoneId="1">
+ <volumeGroups>
+ <group>
+ <device address="bus0_media_out">
+ <context context="music"/>
+ </device>
+ <device address="bus3_call_ring_out">
+ <context context="call_ring"/>
+ </device>
+ </group>
+ <group>
+ <device address="bus1_navigation_out">
+ <context context="navigation"/>
+ </device>
+ </group>
+ </volumeGroups>
+ <displays>
+ <display port="1"/>
+ <display port="2"/>
+ </displays>
+ </zone>
+ <zone name="rear seat zone" audioZoneId="2" occupantZoneId="1">
+ <volumeGroups>
+ <group>
+ <device address="bus100_rear_seat">
+ <context context="music"/>
+ <context context="navigation"/>
+ <context context="voice_command"/>
+ <context context="call_ring"/>
+ <context context="call"/>
+ <context context="alarm"/>
+ <context context="notification"/>
+ <context context="system_sound"/>
+ </device>
+ </group>
+ </volumeGroups>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_empty_input_device.xml b/tests/carservice_test/res/raw/car_audio_configuration_empty_input_device.xml
new file mode 100644
index 0000000..d2ff43d
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_empty_input_device.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<carAudioConfiguration version="2">
+ <zones>
+ <zone name="primary zone" isPrimary="true" audioZoneId="0">
+ <volumeGroups>
+ <group>
+ <device address="bus0_media_out">
+ <context context="music"/>
+ </device>
+ <device address="bus3_call_ring_out">
+ <context context="call_ring"/>
+ </device>
+ </group>
+ <group>
+ <device address="bus1_navigation_out">
+ <context context="navigation"/>
+ </device>
+ </group>
+ </volumeGroups>
+ <inputDevices>
+ <inputDevice address=""/>
+ <inputDevice address="Built-In Mic"/>
+ </inputDevices>
+ </zone>
+ <zone name="rear seat zone" audioZoneId="1">
+ <volumeGroups>
+ <group>
+ <device address="bus100_rear_seat">
+ <context context="music"/>
+ <context context="navigation"/>
+ <context context="voice_command"/>
+ <context context="call_ring"/>
+ <context context="call"/>
+ <context context="alarm"/>
+ <context context="notification"/>
+ <context context="system_sound"/>
+ </device>
+ </group>
+ </volumeGroups>
+ <inputDevices>
+ <inputDevice address="bus_1000_input"/>
+ <inputDevice address="Built-In Back Mic"/>
+ </inputDevices>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_empty_occupant_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_empty_occupant_zone_id.xml
new file mode 100644
index 0000000..c4ef494
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_empty_occupant_zone_id.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<carAudioConfiguration version="2">
+ <zones>
+ <zone name="primary zone" isPrimary="true" audioZoneId="0" occupantZoneId="">
+ <volumeGroups>
+ <group>
+ <device address="bus0_media_out">
+ <context context="music"/>
+ </device>
+ <device address="bus3_call_ring_out">
+ <context context="call_ring"/>
+ </device>
+ </group>
+ <group>
+ <device address="bus1_navigation_out">
+ <context context="navigation"/>
+ </device>
+ </group>
+ </volumeGroups>
+ <displays>
+ <display port="1"/>
+ <display port="2"/>
+ </displays>
+ </zone>
+ <zone name="rear seat zone" audioZoneId="2">
+ <volumeGroups>
+ <group>
+ <device address="bus100_rear_seat">
+ <context context="music"/>
+ <context context="navigation"/>
+ <context context="voice_command"/>
+ <context context="call_ring"/>
+ <context context="call"/>
+ <context context="alarm"/>
+ <context context="notification"/>
+ <context context="system_sound"/>
+ </device>
+ </group>
+ </volumeGroups>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_missing_address.xml b/tests/carservice_test/res/raw/car_audio_configuration_missing_address.xml
new file mode 100644
index 0000000..7af8d68
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_missing_address.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<carAudioConfiguration version="2">
+ <zones>
+ <zone name="primary zone" isPrimary="true" audioZoneId="0">
+ <volumeGroups>
+ <group>
+ <device address="bus0_media_out">
+ <context context="music"/>
+ </device>
+ <device address="bus3_call_ring_out">
+ <context context="call_ring"/>
+ </device>
+ </group>
+ <group>
+ <device address="bus1_navigation_out">
+ <context context="navigation"/>
+ </device>
+ </group>
+ </volumeGroups>
+ <inputDevices>
+ <inputDevice address="fm_tuner"/>
+ <inputDevice/>
+ </inputDevices>
+ </zone>
+ <zone name="rear seat zone" audioZoneId="1">
+ <volumeGroups>
+ <group>
+ <device address="bus100_rear_seat">
+ <context context="music"/>
+ <context context="navigation"/>
+ <context context="voice_command"/>
+ <context context="call_ring"/>
+ <context context="call"/>
+ <context context="alarm"/>
+ <context context="notification"/>
+ <context context="system_sound"/>
+ </device>
+ </group>
+ </volumeGroups>
+ <inputDevices>
+ <inputDevice address="bus_1000_input"/>
+ <inputDevice address="Built-In Back Mic"/>
+ </inputDevices>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_negative_audio_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_negative_audio_zone_id.xml
new file mode 100644
index 0000000..3c202f3
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_negative_audio_zone_id.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<carAudioConfiguration version="2">
+ <zones>
+ <zone name="primary zone" isPrimary="true" audioZoneId="-1" occupantZoneId="1">
+ <volumeGroups>
+ <group>
+ <device address="bus0_media_out">
+ <context context="music"/>
+ </device>
+ <device address="bus3_call_ring_out">
+ <context context="call_ring"/>
+ </device>
+ </group>
+ <group>
+ <device address="bus1_navigation_out">
+ <context context="navigation"/>
+ </device>
+ </group>
+ </volumeGroups>
+ <displays>
+ <display port="1"/>
+ <display port="2"/>
+ </displays>
+ </zone>
+ <zone name="rear seat zone" audioZoneId="2">
+ <volumeGroups>
+ <group>
+ <device address="bus100_rear_seat">
+ <context context="music"/>
+ <context context="navigation"/>
+ <context context="voice_command"/>
+ <context context="call_ring"/>
+ <context context="call"/>
+ <context context="alarm"/>
+ <context context="notification"/>
+ <context context="system_sound"/>
+ </device>
+ </group>
+ </volumeGroups>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_negative_occupant_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_negative_occupant_zone_id.xml
new file mode 100644
index 0000000..ac240d6
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_negative_occupant_zone_id.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<carAudioConfiguration version="2">
+ <zones>
+ <zone name="primary zone" isPrimary="true" audioZoneId="0" occupantZoneId="-1">
+ <volumeGroups>
+ <group>
+ <device address="bus0_media_out">
+ <context context="music"/>
+ </device>
+ <device address="bus3_call_ring_out">
+ <context context="call_ring"/>
+ </device>
+ </group>
+ <group>
+ <device address="bus1_navigation_out">
+ <context context="navigation"/>
+ </device>
+ </group>
+ </volumeGroups>
+ <displays>
+ <display port="1"/>
+ <display port="2"/>
+ </displays>
+ </zone>
+ <zone name="rear seat zone" audioZoneId="2">
+ <volumeGroups>
+ <group>
+ <device address="bus100_rear_seat">
+ <context context="music"/>
+ <context context="navigation"/>
+ <context context="voice_command"/>
+ <context context="call_ring"/>
+ <context context="call"/>
+ <context context="alarm"/>
+ <context context="notification"/>
+ <context context="system_sound"/>
+ </device>
+ </group>
+ </volumeGroups>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_no_audio_zone_id_for_primary_zone.xml b/tests/carservice_test/res/raw/car_audio_configuration_no_audio_zone_id_for_primary_zone.xml
new file mode 100644
index 0000000..720e875
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_no_audio_zone_id_for_primary_zone.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<carAudioConfiguration version="2">
+ <zones>
+ <zone name="primary zone" isPrimary="true">
+ <volumeGroups>
+ <group>
+ <device address="bus0_media_out">
+ <context context="music"/>
+ </device>
+ <device address="bus3_call_ring_out">
+ <context context="call_ring"/>
+ </device>
+ </group>
+ <group>
+ <device address="bus1_navigation_out">
+ <context context="navigation"/>
+ </device>
+ </group>
+ </volumeGroups>
+ </zone>
+ <zone name="rear seat zone" audioZoneId="2"
+ occupantZoneId="1">
+ <volumeGroups>
+ <group>
+ <device address="bus100_rear_seat">
+ <context context="music"/>
+ <context context="navigation"/>
+ <context context="voice_command"/>
+ <context context="call_ring"/>
+ <context context="call"/>
+ <context context="alarm"/>
+ <context context="notification"/>
+ <context context="system_sound"/>
+ </device>
+ </group>
+ </volumeGroups>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_non_existent_input_device.xml b/tests/carservice_test/res/raw/car_audio_configuration_non_existent_input_device.xml
new file mode 100644
index 0000000..2f317d0
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_non_existent_input_device.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<carAudioConfiguration version="2">
+ <zones>
+ <zone name="primary zone" isPrimary="true">
+ <volumeGroups>
+ <group>
+ <device address="bus0_media_out">
+ <context context="music"/>
+ </device>
+ <device address="bus3_call_ring_out">
+ <context context="call_ring"/>
+ </device>
+ </group>
+ <group>
+ <device address="bus1_navigation_out">
+ <context context="navigation"/>
+ </device>
+ </group>
+ </volumeGroups>
+ <inputDevices>
+ <inputDevice address="fm_tuner"/>
+ <inputDevice address="Built-In Mic"/>
+ </inputDevices>
+ </zone>
+ <zone name="rear seat zone" audioZoneId="1">
+ <volumeGroups>
+ <group>
+ <device address="bus100_rear_seat">
+ <context context="music"/>
+ <context context="navigation"/>
+ <context context="voice_command"/>
+ <context context="call_ring"/>
+ <context context="call"/>
+ <context context="alarm"/>
+ <context context="notification"/>
+ <context context="system_sound"/>
+ </device>
+ </group>
+ </volumeGroups>
+ <inputDevices>
+ <inputDevice address="MISSING_AUDIO_INPUT"/>
+ <inputDevice address="Built-In Back Mic"/>
+ </inputDevices>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_audio_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_audio_zone_id.xml
new file mode 100644
index 0000000..2721b50
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_audio_zone_id.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<carAudioConfiguration version="2">
+ <zones>
+ <zone name="primary zone" isPrimary="true" audioZoneId="primary" occupantZoneId="1">
+ <volumeGroups>
+ <group>
+ <device address="bus0_media_out">
+ <context context="music"/>
+ </device>
+ <device address="bus3_call_ring_out">
+ <context context="call_ring"/>
+ </device>
+ </group>
+ <group>
+ <device address="bus1_navigation_out">
+ <context context="navigation"/>
+ </device>
+ </group>
+ </volumeGroups>
+ <displays>
+ <display port="1"/>
+ <display port="2"/>
+ </displays>
+ </zone>
+ <zone name="rear seat zone" audioZoneId="2">
+ <volumeGroups>
+ <group>
+ <device address="bus100_rear_seat">
+ <context context="music"/>
+ <context context="navigation"/>
+ <context context="voice_command"/>
+ <context context="call_ring"/>
+ <context context="call"/>
+ <context context="alarm"/>
+ <context context="notification"/>
+ <context context="system_sound"/>
+ </device>
+ </group>
+ </volumeGroups>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_occupant_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_occupant_zone_id.xml
new file mode 100644
index 0000000..85dd768
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_occupant_zone_id.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<carAudioConfiguration version="2">
+ <zones>
+ <zone name="primary zone" isPrimary="true" audioZoneId="0" occupantZoneId="one">
+ <volumeGroups>
+ <group>
+ <device address="bus0_media_out">
+ <context context="music"/>
+ </device>
+ <device address="bus3_call_ring_out">
+ <context context="call_ring"/>
+ </device>
+ </group>
+ <group>
+ <device address="bus1_navigation_out">
+ <context context="navigation"/>
+ </device>
+ </group>
+ </volumeGroups>
+ <displays>
+ <display port="1"/>
+ <display port="2"/>
+ </displays>
+ </zone>
+ <zone name="rear seat zone" audioZoneId="2">
+ <volumeGroups>
+ <group>
+ <device address="bus100_rear_seat">
+ <context context="music"/>
+ <context context="navigation"/>
+ <context context="voice_command"/>
+ <context context="call_ring"/>
+ <context context="call"/>
+ <context context="alarm"/>
+ <context context="notification"/>
+ <context context="system_sound"/>
+ </device>
+ </group>
+ </volumeGroups>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_non_primary_zone_with_primary_audio_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_non_primary_zone_with_primary_audio_zone_id.xml
new file mode 100644
index 0000000..d6889f6
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_non_primary_zone_with_primary_audio_zone_id.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<carAudioConfiguration version="2">
+ <zones>
+ <zone name="primary zone" isPrimary="true" occupantZoneId="1">
+ <volumeGroups>
+ <group>
+ <device address="bus0_media_out">
+ <context context="music"/>
+ </device>
+ <device address="bus3_call_ring_out">
+ <context context="call_ring"/>
+ </device>
+ </group>
+ <group>
+ <device address="bus1_navigation_out">
+ <context context="navigation"/>
+ </device>
+ </group>
+ </volumeGroups>
+ <displays>
+ <display port="1"/>
+ <display port="2"/>
+ </displays>
+ </zone>
+ <zone name="rear seat zone" audioZoneId="0">
+ <volumeGroups>
+ <group>
+ <device address="bus100_rear_seat">
+ <context context="music"/>
+ <context context="navigation"/>
+ <context context="voice_command"/>
+ <context context="call_ring"/>
+ <context context="call"/>
+ <context context="alarm"/>
+ <context context="notification"/>
+ <context context="system_sound"/>
+ </device>
+ </group>
+ </volumeGroups>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_primary_zone_with_non_zero_audio_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_primary_zone_with_non_zero_audio_zone_id.xml
new file mode 100644
index 0000000..ee0b268
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_primary_zone_with_non_zero_audio_zone_id.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<carAudioConfiguration version="2">
+ <zones>
+ <zone name="primary zone" isPrimary="true" audioZoneId="1" occupantZoneId="1">
+ <volumeGroups>
+ <group>
+ <device address="bus0_media_out">
+ <context context="music"/>
+ </device>
+ <device address="bus3_call_ring_out">
+ <context context="call_ring"/>
+ </device>
+ </group>
+ <group>
+ <device address="bus1_navigation_out">
+ <context context="navigation"/>
+ </device>
+ </group>
+ </volumeGroups>
+ <displays>
+ <display port="1"/>
+ <display port="2"/>
+ </displays>
+ </zone>
+ <zone name="rear seat zone" audioZoneId="2">
+ <volumeGroups>
+ <group>
+ <device address="bus100_rear_seat">
+ <context context="music"/>
+ <context context="navigation"/>
+ <context context="voice_command"/>
+ <context context="call_ring"/>
+ <context context="call"/>
+ <context context="alarm"/>
+ <context context="notification"/>
+ <context context="system_sound"/>
+ </device>
+ </group>
+ </volumeGroups>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_repeat_input_device.xml b/tests/carservice_test/res/raw/car_audio_configuration_repeat_input_device.xml
new file mode 100644
index 0000000..1934fe7
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_repeat_input_device.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<carAudioConfiguration version="2">
+ <zones>
+ <zone name="primary zone" isPrimary="true" audioZoneId="0">
+ <volumeGroups>
+ <group>
+ <device address="bus0_media_out">
+ <context context="music"/>
+ </device>
+ <device address="bus3_call_ring_out">
+ <context context="call_ring"/>
+ </device>
+ </group>
+ <group>
+ <device address="bus1_navigation_out">
+ <context context="navigation"/>
+ </device>
+ </group>
+ </volumeGroups>
+ <inputDevices>
+ <inputDevice address="fm_tuner"/>
+ <inputDevice address="Built-In Mic"/>
+ </inputDevices>
+ </zone>
+ <zone name="rear seat zone" audioZoneId="1">
+ <volumeGroups>
+ <group>
+ <device address="bus100_rear_seat">
+ <context context="music"/>
+ <context context="navigation"/>
+ <context context="voice_command"/>
+ <context context="call_ring"/>
+ <context context="call"/>
+ <context context="alarm"/>
+ <context context="notification"/>
+ <context context="system_sound"/>
+ </device>
+ </group>
+ </volumeGroups>
+ <inputDevices>
+ <inputDevice address="fm_tuner"/>
+ <inputDevice address="Built-In Back Mic"/>
+ </inputDevices>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_with_input_devices.xml b/tests/carservice_test/res/raw/car_audio_configuration_with_input_devices.xml
new file mode 100644
index 0000000..3b6a013
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_with_input_devices.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<carAudioConfiguration version="2">
+ <zones>
+ <zone name="primary zone" isPrimary="true" audioZoneId="0">
+ <volumeGroups>
+ <group>
+ <device address="bus0_media_out">
+ <context context="music"/>
+ </device>
+ <device address="bus3_call_ring_out">
+ <context context="call_ring"/>
+ </device>
+ </group>
+ <group>
+ <device address="bus1_navigation_out">
+ <context context="navigation"/>
+ </device>
+ </group>
+ </volumeGroups>
+ <inputDevices>
+ <inputDevice address="fm_tuner"/>
+ <inputDevice address="Built-In Mic"/>
+ </inputDevices>
+ </zone>
+ <zone name="rear seat zone" audioZoneId="1">
+ <volumeGroups>
+ <group>
+ <device address="bus100_rear_seat">
+ <context context="music"/>
+ <context context="navigation"/>
+ <context context="voice_command"/>
+ <context context="call_ring"/>
+ <context context="call"/>
+ <context context="alarm"/>
+ <context context="notification"/>
+ <context context="system_sound"/>
+ </device>
+ </group>
+ </volumeGroups>
+ <inputDevices>
+ <inputDevice address="bus_1000_input"/>
+ <inputDevice address="Built-In Back Mic"/>
+ </inputDevices>
+ </zone>
+ </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/src/com/android/car/CarDiagnosticManagerTest.java b/tests/carservice_test/src/com/android/car/CarDiagnosticManagerTest.java
index fbb7954..fb7eee6 100644
--- a/tests/carservice_test/src/com/android/car/CarDiagnosticManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/CarDiagnosticManagerTest.java
@@ -204,6 +204,13 @@
}
@Override
+ protected synchronized void configureResourceOverrides(MockResources resources) {
+ super.configureResourceOverrides(resources);
+ resources.overrideResource(com.android.car.R.array.config_allowed_optional_car_features,
+ new String[]{Car.DIAGNOSTIC_SERVICE});
+ }
+
+ @Override
protected synchronized void configureMockedHal() {
java.util.Collection<Integer> numVendorSensors = Arrays.asList(0, 0);
java.util.Collection<Integer> selectiveClear = Collections.singletonList(1);
diff --git a/tests/carservice_test/src/com/android/car/CarStorageMonitoringTest.java b/tests/carservice_test/src/com/android/car/CarStorageMonitoringTest.java
index 8ee1f85..1c7c9e9 100644
--- a/tests/carservice_test/src/com/android/car/CarStorageMonitoringTest.java
+++ b/tests/carservice_test/src/com/android/car/CarStorageMonitoringTest.java
@@ -371,6 +371,8 @@
@Override
protected synchronized void configureResourceOverrides(MockResources resources) {
super.configureResourceOverrides(resources);
+ resources.overrideResource(com.android.car.R.array.config_allowed_optional_car_features,
+ new String[] {Car.STORAGE_MONITORING_SERVICE});
final ResourceOverrides overrides = PER_TEST_RESOURCES.getOrDefault(getName(), null);
if (overrides != null) {
overrides.overrideResources(resources);
diff --git a/tests/carservice_test/src/com/android/car/MockedVmsTestBase.java b/tests/carservice_test/src/com/android/car/MockedVmsTestBase.java
index f31e467..17ec017 100644
--- a/tests/carservice_test/src/com/android/car/MockedVmsTestBase.java
+++ b/tests/carservice_test/src/com/android/car/MockedVmsTestBase.java
@@ -63,6 +63,8 @@
@Override
protected synchronized void configureResourceOverrides(MockResources resources) {
super.configureResourceOverrides(resources);
+ resources.overrideResource(com.android.car.R.array.config_allowed_optional_car_features,
+ new String[] {Car.VMS_SUBSCRIBER_SERVICE});
// Override publisher client endpoint configurations
// Both lists must be set, but only one will be used (see setUp)
resources.overrideResource(com.android.car.R.array.vmsPublisherSystemClients,
diff --git a/tests/carservice_test/src/com/android/car/VmsPublisherClientServiceTest.java b/tests/carservice_test/src/com/android/car/VmsPublisherClientServiceTest.java
index 46907f0..2b1129d 100644
--- a/tests/carservice_test/src/com/android/car/VmsPublisherClientServiceTest.java
+++ b/tests/carservice_test/src/com/android/car/VmsPublisherClientServiceTest.java
@@ -27,7 +27,6 @@
import android.hardware.automotive.vehicle.V2_0.VmsMessageWithLayerIntegerValuesIndex;
import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import org.junit.Test;
@@ -47,8 +46,7 @@
public static final byte[] PAYLOAD = new byte[]{1, 1, 2, 3, 5, 8, 13};
@Test
- @FlakyTest
- public void testPublish() throws Exception {
+ public void testPublish() {
MockHalClient client = getMockHalClient();
client.sendMessage(
VmsMessageType.SUBSCRIBE,
@@ -56,6 +54,7 @@
MOCK_PUBLISHER_LAYER_SUBTYPE,
MOCK_PUBLISHER_LAYER_VERSION);
+ getMockPublisherClient().receiveSubscriptionState(); // Wait for subscription to propagate
getMockPublisherClient().publish(MOCK_PUBLISHER_LAYER, MOCK_PUBLISHER_ID, PAYLOAD);
VehiclePropValue message;
diff --git a/tests/carservice_test/src/com/android/car/audio/CarAudioZonesHelperTest.java b/tests/carservice_test/src/com/android/car/audio/CarAudioZonesHelperTest.java
index c87ad94..614d941 100644
--- a/tests/carservice_test/src/com/android/car/audio/CarAudioZonesHelperTest.java
+++ b/tests/carservice_test/src/com/android/car/audio/CarAudioZonesHelperTest.java
@@ -15,16 +15,24 @@
*/
package com.android.car.audio;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.TestCase.fail;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.testng.Assert.expectThrows;
+import android.car.media.CarAudioManager;
import android.content.Context;
import android.hardware.automotive.audiocontrol.V1_0.ContextNumber;
+import android.media.AudioDeviceAddress;
+import android.media.AudioDeviceInfo;
+import android.util.SparseIntArray;
import android.view.DisplayAddress;
import androidx.test.core.app.ApplicationProvider;
@@ -38,16 +46,18 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mockito;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.InputStream;
+import java.util.Arrays;
import java.util.List;
+import java.util.stream.Collectors;
@RunWith(AndroidJUnit4.class)
public class CarAudioZonesHelperTest {
- private List<CarAudioDeviceInfo> mCarAudioDeviceInfos;
+ private List<CarAudioDeviceInfo> mCarAudioOutputDeviceInfos;
+ private AudioDeviceInfo[] mInputAudioDeviceInfos;
private Context mContext;
private InputStream mInputStream;
private static final String BUS_0_ADDRESS = "bus0_media_out";
@@ -55,9 +65,18 @@
private static final String BUS_3_ADDRESS = "bus3_call_ring_out";
private static final String BUS_100_ADDRESS = "bus100_rear_seat";
+ private static final String PRIMARY_ZONE_MICROPHONE_ADDRESS = "Built-In Mic";
+ private static final String PRIMARY_ZONE_FM_TUNER_ADDRESS = "fm_tuner";
+ private static final String SECONDARY_ZONE_BACK_MICROPHONE_ADDRESS = "Built-In Back Mic";
+ private static final String SECONDARY_ZONE_BUS_1000_INPUT_ADDRESS = "bus_1000_input";
+
+ private static final int PRIMARY_OCCUPANT_ID = 1;
+ private static final int SECONDARY_ZONE_ID = 2;
+
@Before
public void setUp() {
- mCarAudioDeviceInfos = generateCarDeviceInfos();
+ mCarAudioOutputDeviceInfos = generateCarDeviceInfos();
+ mInputAudioDeviceInfos = generateInputDeviceInfos();
mContext = ApplicationProvider.getApplicationContext();
mInputStream = mContext.getResources().openRawResource(R.raw.car_audio_configuration);
}
@@ -79,12 +98,24 @@
generateCarAudioDeviceInfo(""),
generateCarAudioDeviceInfo(null),
generateCarAudioDeviceInfo(null)
-
);
}
+ private AudioDeviceInfo[] generateInputDeviceInfos() {
+ return new AudioDeviceInfo[] {
+ generateInputAudioDeviceInfo(PRIMARY_ZONE_MICROPHONE_ADDRESS,
+ AudioDeviceInfo.TYPE_BUILTIN_MIC),
+ generateInputAudioDeviceInfo(PRIMARY_ZONE_FM_TUNER_ADDRESS,
+ AudioDeviceInfo.TYPE_FM_TUNER),
+ generateInputAudioDeviceInfo(SECONDARY_ZONE_BACK_MICROPHONE_ADDRESS,
+ AudioDeviceInfo.TYPE_BUS),
+ generateInputAudioDeviceInfo(SECONDARY_ZONE_BUS_1000_INPUT_ADDRESS,
+ AudioDeviceInfo.TYPE_BUILTIN_MIC)
+ };
+ }
+
private CarAudioDeviceInfo generateCarAudioDeviceInfo(String address) {
- CarAudioDeviceInfo cadiMock = Mockito.mock(CarAudioDeviceInfo.class);
+ CarAudioDeviceInfo cadiMock = mock(CarAudioDeviceInfo.class);
when(cadiMock.getStepValue()).thenReturn(1);
when(cadiMock.getDefaultGain()).thenReturn(2);
when(cadiMock.getMaxGain()).thenReturn(5);
@@ -93,20 +124,72 @@
return cadiMock;
}
+ private AudioDeviceInfo generateInputAudioDeviceInfo(String address, int type) {
+ AudioDeviceInfo inputMock = mock(AudioDeviceInfo.class);
+ when(inputMock.getAddress()).thenReturn(address);
+ when(inputMock.getType()).thenReturn(type);
+ when(inputMock.isSource()).thenReturn(true);
+ when(inputMock.isSink()).thenReturn(false);
+ return inputMock;
+ }
+
@Test
- public void loadAudioZones_parsesAllZones() throws IOException, XmlPullParserException {
+ public void loadAudioZones_parsesAllZones() throws Exception {
CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, mInputStream,
- mCarAudioDeviceInfos);
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+
+ CarAudioZone[] zones = cazh.loadAudioZones();
+
+ assertThat(zones.length).isEqualTo(2);
+ }
+
+ @Test
+ public void loadAudioZones_versionOneParsesAllZones() throws Exception {
+ try (InputStream versionOneStream = mContext.getResources().openRawResource(
+ R.raw.car_audio_configuration_V1)) {
+ CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, versionOneStream,
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+
+ CarAudioZone[] zones = cazh.loadAudioZones();
+
+ assertThat(zones.length).isEqualTo(2);
+ }
+ }
+
+ @Test
+ public void loadAudioZones_parsesAudioZoneId() throws Exception {
+ CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, mInputStream,
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+
+ CarAudioZone[] zones = cazh.loadAudioZones();
+
+ List<Integer> zoneIds = getListOfZoneIds(zones);
+ assertThat(zoneIds.size()).isEqualTo(2);
+ assertThat(zoneIds)
+ .containsAllOf(CarAudioManager.PRIMARY_AUDIO_ZONE, SECONDARY_ZONE_ID).inOrder();
+ }
+
+ @Test
+ public void loadAudioZones_parsesOccupantZoneId() throws Exception {
+ CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, mInputStream,
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
CarAudioZone[] zones = cazh.loadAudioZones();
assertEquals(2, zones.length);
+
+ SparseIntArray audioZoneIdToOccupantZoneIdMapping =
+ cazh.getCarAudioZoneIdToOccupantZoneIdMapping();
+ assertThat(audioZoneIdToOccupantZoneIdMapping.get(CarAudioManager.PRIMARY_AUDIO_ZONE))
+ .isEqualTo(PRIMARY_OCCUPANT_ID);
+ assertThat(audioZoneIdToOccupantZoneIdMapping.get(SECONDARY_ZONE_ID, -1))
+ .isEqualTo(-1);
}
@Test
public void loadAudioZones_parsesZoneName() throws IOException, XmlPullParserException {
CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, mInputStream,
- mCarAudioDeviceInfos);
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
CarAudioZone[] zones = cazh.loadAudioZones();
@@ -117,7 +200,7 @@
@Test
public void loadAudioZones_parsesIsPrimary() throws IOException, XmlPullParserException {
CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, mInputStream,
- mCarAudioDeviceInfos);
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
CarAudioZone[] zones = cazh.loadAudioZones();
@@ -131,7 +214,7 @@
@Test
public void loadAudioZones_parsesVolumeGroups() throws IOException, XmlPullParserException {
CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, mInputStream,
- mCarAudioDeviceInfos);
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
CarAudioZone[] zones = cazh.loadAudioZones();
@@ -142,7 +225,7 @@
@Test
public void loadAudioZones_parsesAddresses() throws IOException, XmlPullParserException {
CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, mInputStream,
- mCarAudioDeviceInfos);
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
CarAudioZone[] zones = cazh.loadAudioZones();
@@ -157,7 +240,7 @@
@Test
public void loadAudioZones_parsesContexts() throws IOException, XmlPullParserException {
CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, mInputStream,
- mCarAudioDeviceInfos);
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
CarAudioZone[] zones = cazh.loadAudioZones();
@@ -179,7 +262,7 @@
public void loadAudioZones_parsesPhysicalDisplayAddresses()
throws IOException, XmlPullParserException {
CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, mInputStream,
- mCarAudioDeviceInfos);
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
CarAudioZone[] zones = cazh.loadAudioZones();
@@ -194,7 +277,7 @@
public void loadAudioZones_defaultsDisplayAddressesToEmptyList()
throws IOException, XmlPullParserException {
CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, mInputStream,
- mCarAudioDeviceInfos);
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
CarAudioZone[] zones = cazh.loadAudioZones();
@@ -208,7 +291,7 @@
try (InputStream duplicatePortStream = mContext.getResources().openRawResource(
R.raw.car_audio_configuration_duplicate_ports)) {
CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, duplicatePortStream,
- mCarAudioDeviceInfos);
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
cazh.loadAudioZones();
}
@@ -220,7 +303,7 @@
try (InputStream duplicatePortStream = mContext.getResources().openRawResource(
R.raw.car_audio_configuration_non_numerical_port)) {
CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, duplicatePortStream,
- mCarAudioDeviceInfos);
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
try {
cazh.loadAudioZones();
@@ -230,4 +313,258 @@
}
}
}
+
+ @Test
+ public void loadAudioZones_passesOnMissingAudioZoneIdForPrimary() throws Exception {
+ try (InputStream missingAudioZoneIdStream = mContext.getResources().openRawResource(
+ R.raw.car_audio_configuration_no_audio_zone_id_for_primary_zone)) {
+ CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, missingAudioZoneIdStream,
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+
+ CarAudioZone[] zones = cazh.loadAudioZones();
+
+ List<Integer> zoneIds = getListOfZoneIds(zones);
+ assertThat(zoneIds.size()).isEqualTo(2);
+ assertThat(zoneIds).contains(CarAudioManager.PRIMARY_AUDIO_ZONE);
+ assertThat(zoneIds).contains(SECONDARY_ZONE_ID);
+ }
+ }
+
+ @Test
+ public void loadAudioZones_versionOneFailsOnAudioZoneId() throws Exception {
+ try (InputStream versionOneAudioZoneIdStream = mContext.getResources().openRawResource(
+ R.raw.car_audio_configuration_V1_with_audio_zone_id)) {
+ CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext,
+ versionOneAudioZoneIdStream, mCarAudioOutputDeviceInfos,
+ mInputAudioDeviceInfos);
+ IllegalArgumentException thrown =
+ expectThrows(IllegalArgumentException.class,
+ () -> cazh.loadAudioZones());
+ assertThat(thrown).hasMessageThat().contains("Invalid audio attribute audioZoneId");
+ }
+ }
+
+ @Test
+ public void loadAudioZones_versionOneFailsOnOccupantZoneId() throws Exception {
+ try (InputStream versionOneOccupantIdStream = mContext.getResources().openRawResource(
+ R.raw.car_audio_configuration_V1_with_occupant_zone_id)) {
+ CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, versionOneOccupantIdStream,
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+ IllegalArgumentException thrown =
+ expectThrows(IllegalArgumentException.class,
+ () -> cazh.loadAudioZones());
+ assertThat(thrown).hasMessageThat().contains("Invalid audio attribute occupantZoneId");
+ }
+ }
+
+ @Test
+ public void loadAudioZones_parsesInputDevices() throws Exception {
+ try (InputStream inputDevicesStream = mContext.getResources().openRawResource(
+ R.raw.car_audio_configuration_with_input_devices)) {
+ CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, inputDevicesStream,
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+
+ CarAudioZone[] zones = cazh.loadAudioZones();
+
+ CarAudioZone primaryZone = zones[0];
+ List<AudioDeviceAddress> primaryZoneInputDevices =
+ primaryZone.getInputAudioDeviceAddresses();
+ assertThat(primaryZoneInputDevices).hasSize(2);
+
+ List<String> primaryZoneInputAddresses =
+ primaryZoneInputDevices.stream().map(a ->a.getAddress()).collect(
+ Collectors.toList());
+ assertThat(primaryZoneInputAddresses).containsAllOf(PRIMARY_ZONE_FM_TUNER_ADDRESS,
+ PRIMARY_ZONE_MICROPHONE_ADDRESS).inOrder();
+
+ CarAudioZone secondaryZone = zones[1];
+ List<AudioDeviceAddress> secondaryZoneInputDevices =
+ secondaryZone.getInputAudioDeviceAddresses();
+ List<String> secondaryZoneInputAddresses =
+ secondaryZoneInputDevices.stream().map(a ->a.getAddress()).collect(
+ Collectors.toList());
+ assertThat(secondaryZoneInputAddresses).containsAllOf(
+ SECONDARY_ZONE_BUS_1000_INPUT_ADDRESS,
+ SECONDARY_ZONE_BACK_MICROPHONE_ADDRESS).inOrder();
+ }
+ }
+
+ @Test
+ public void loadAudioZones_failsOnDuplicateOccupantZoneId() throws Exception {
+ try (InputStream duplicateOccupantZoneIdStream = mContext.getResources().openRawResource(
+ R.raw.car_audio_configuration_duplicate_occupant_zone_id)) {
+ CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext,
+ duplicateOccupantZoneIdStream, mCarAudioOutputDeviceInfos,
+ mInputAudioDeviceInfos);
+ IllegalArgumentException thrown =
+ expectThrows(IllegalArgumentException.class,
+ () -> cazh.loadAudioZones());
+ assertThat(thrown).hasMessageThat().contains("already associated with a zone");
+ }
+ }
+
+ @Test
+ public void loadAudioZones_failsOnDuplicateAudioZoneId() throws Exception {
+ try (InputStream duplicateAudioZoneIdStream = mContext.getResources().openRawResource(
+ R.raw.car_audio_configuration_duplicate_audio_zone_id)) {
+ CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, duplicateAudioZoneIdStream,
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+ IllegalArgumentException thrown =
+ expectThrows(IllegalArgumentException.class,
+ () -> cazh.loadAudioZones());
+ assertThat(thrown).hasMessageThat().contains("already associated with a zone");
+ }
+ }
+ @Test
+ public void loadAudioZones_failsOnEmptyInputDeviceAddress() throws Exception {
+ try (InputStream inputDevicesStream = mContext.getResources().openRawResource(
+ R.raw.car_audio_configuration_empty_input_device)) {
+ CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, inputDevicesStream,
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+
+ IllegalArgumentException thrown =
+ expectThrows(IllegalArgumentException.class,
+ () -> cazh.loadAudioZones());
+ assertThat(thrown).hasMessageThat().contains("empty.");
+ }
+ }
+
+ @Test
+ public void loadAudioZones_failsOnNonNumericalAudioZoneId() throws Exception {
+ try (InputStream nonNumericalStream = mContext.getResources().openRawResource(
+ R.raw.car_audio_configuration_non_numerical_audio_zone_id)) {
+ CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, nonNumericalStream,
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+ IllegalArgumentException thrown =
+ expectThrows(IllegalArgumentException.class,
+ () -> cazh.loadAudioZones());
+ assertThat(thrown).hasMessageThat().contains("was \"primary\" instead.");
+ }
+ }
+
+ @Test
+ public void loadAudioZones_failsOnNegativeAudioZoneId() throws Exception {
+ try (InputStream negativeAudioZoneIdStream = mContext.getResources().openRawResource(
+ R.raw.car_audio_configuration_negative_audio_zone_id)) {
+ CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, negativeAudioZoneIdStream,
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+ IllegalArgumentException thrown =
+ expectThrows(IllegalArgumentException.class,
+ () -> cazh.loadAudioZones());
+ assertThat(thrown).hasMessageThat().contains("but was \"-1\" instead.");
+ }
+ }
+
+ @Test
+ public void loadAudioZones_failsOnMissingInputDevice() throws Exception {
+ try (InputStream inputDevicesStream = mContext.getResources().openRawResource(
+ R.raw.car_audio_configuration_missing_address)) {
+ CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, inputDevicesStream,
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+
+ NullPointerException thrown =
+ expectThrows(NullPointerException.class,
+ () -> cazh.loadAudioZones());
+ assertThat(thrown).hasMessageThat().contains("present.");
+ }
+ }
+
+ @Test
+ public void loadAudioZones_failsOnNonNumericalOccupantZoneId() throws Exception {
+ try (InputStream nonNumericalStream = mContext.getResources().openRawResource(
+ R.raw.car_audio_configuration_non_numerical_occupant_zone_id)) {
+ CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, nonNumericalStream,
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+ IllegalArgumentException thrown =
+ expectThrows(IllegalArgumentException.class,
+ () -> cazh.loadAudioZones());
+ assertThat(thrown).hasMessageThat().contains("was \"one\" instead.");
+ }
+ }
+
+ @Test
+ public void loadAudioZones_failsOnNegativeOccupantZoneId() throws Exception {
+ try (InputStream negativeOccupantZoneIdStream = mContext.getResources().openRawResource(
+ R.raw.car_audio_configuration_negative_occupant_zone_id)) {
+ CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext,
+ negativeOccupantZoneIdStream, mCarAudioOutputDeviceInfos,
+ mInputAudioDeviceInfos);
+ IllegalArgumentException thrown =
+ expectThrows(IllegalArgumentException.class,
+ () -> cazh.loadAudioZones());
+ assertThat(thrown).hasMessageThat().contains("was \"-1\" instead.");
+ }
+ }
+
+ @Test
+ public void loadAudioZones_failsOnNonExistentInputDevice() throws Exception {
+ try (InputStream inputDevicesStream = mContext.getResources().openRawResource(
+ R.raw.car_audio_configuration_non_existent_input_device)) {
+ CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, inputDevicesStream,
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+
+ IllegalArgumentException thrown =
+ expectThrows(IllegalArgumentException.class,
+ () -> cazh.loadAudioZones());
+ assertThat(thrown).hasMessageThat().contains("does not exist");
+ }
+ }
+
+ @Test
+ public void loadAudioZones_failsOnEmptyOccupantZoneId() throws Exception {
+ try (InputStream emptyOccupantZoneIdStream = mContext.getResources().openRawResource(
+ R.raw.car_audio_configuration_empty_occupant_zone_id)) {
+ CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, emptyOccupantZoneIdStream,
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+ IllegalArgumentException thrown =
+ expectThrows(IllegalArgumentException.class,
+ () -> cazh.loadAudioZones());
+ assertThat(thrown).hasMessageThat().contains("but was \"\" instead.");
+ }
+ }
+
+ @Test
+ public void loadAudioZones_failsOnNonZeroAudioZoneIdForPrimary() throws Exception {
+ try (InputStream nonZeroForPrimaryStream = mContext.getResources().openRawResource(
+ R.raw.car_audio_configuration_primary_zone_with_non_zero_audio_zone_id)) {
+ CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, nonZeroForPrimaryStream,
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+ IllegalArgumentException thrown =
+ expectThrows(IllegalArgumentException.class,
+ () -> cazh.loadAudioZones());
+ assertThat(thrown).hasMessageThat().contains("it can be left empty.");
+ }
+ }
+
+ @Test
+ public void loadAudioZones_failsOnZeroAudioZoneIdForSecondary() throws Exception {
+ try (InputStream zeroZoneIdForSecondaryStream = mContext.getResources().openRawResource(
+ R.raw.car_audio_configuration_non_primary_zone_with_primary_audio_zone_id)) {
+ CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext,
+ zeroZoneIdForSecondaryStream, mCarAudioOutputDeviceInfos,
+ mInputAudioDeviceInfos);
+ IllegalArgumentException thrown =
+ expectThrows(IllegalArgumentException.class,
+ () -> cazh.loadAudioZones());
+ assertThat(thrown).hasMessageThat().contains("for primary zone");
+ }
+ }
+
+ @Test
+ public void loadAudioZones_failsOnRepeatedInputDevice() throws Exception {
+ try (InputStream inputDevicesStream = mContext.getResources().openRawResource(
+ R.raw.car_audio_configuration_repeat_input_device)) {
+ CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, inputDevicesStream,
+ mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+
+ IllegalArgumentException thrown =
+ expectThrows(IllegalArgumentException.class,
+ () -> cazh.loadAudioZones());
+ assertThat(thrown).hasMessageThat().contains("can not repeat.");
+ }
+ }
+
+ private List<Integer> getListOfZoneIds(CarAudioZone[] zones) {
+ return Arrays.stream(zones).map(CarAudioZone::getId).collect(Collectors.toList());
+ }
}
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 6269f54..35a4b3b 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
@@ -20,7 +20,9 @@
import static android.os.UserHandle.USER_SYSTEM;
import static android.os.UserManager.USER_TYPE_FULL_GUEST;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.google.common.truth.Truth.assertThat;
@@ -31,7 +33,6 @@
import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static java.lang.annotation.ElementType.METHOD;
@@ -66,7 +67,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
@@ -75,6 +75,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoSession;
+import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.quality.Strictness;
@@ -84,6 +85,8 @@
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
@@ -123,6 +126,10 @@
// Value used to set config_disableUserSwitchDuringResume - must be defined before initTest();
private boolean mDisableUserSwitchDuringResume;
+ // Tracks Log.wtf() calls made during code execution / used on verifyWtfNeverLogged()
+ // TODO: move mechanism to common code / custom Rule
+ private final List<UnsupportedOperationException> mWtfs = new ArrayList<>();
+
@Rule
public final TestRule setWakeupTimeRule = new TestWatcher() {
protected void starting(Description description) {
@@ -145,6 +152,7 @@
mSession = mockitoSession()
.strictness(Strictness.LENIENT)
.spyStatic(ActivityManager.class)
+ .spyStatic(Log.class)
.startMocking();
mPowerHal = new MockedPowerHalService(true /*isPowerStateSupported*/,
true /*isDeepSleepAllowed*/, true /*isTimedWakeupAllowed*/);
@@ -153,6 +161,12 @@
.withSystemStateInterface(mSystemStateInterface)
.withWakeLockInterface(mWakeLockInterface)
.withIOInterface(mIOInterface).build();
+ doAnswer((invocation) -> {
+ return addWtf(invocation);
+ }).when(() -> Log.wtf(anyString(), anyString()));
+ doAnswer((invocation) -> {
+ return addWtf(invocation);
+ }).when(() -> Log.wtf(anyString(), anyString(), notNull()));
}
@After
@@ -164,6 +178,14 @@
mSession.finishMocking();
}
+
+ private Object addWtf(InvocationOnMock invocation) {
+ String message = "Called " + invocation;
+ Log.d(TAG, message); // Log always, as some test expect it
+ mWtfs.add(new UnsupportedOperationException(message));
+ return null;
+ }
+
/**
* Helper method to create mService and initialize a test case
*/
@@ -190,9 +212,21 @@
assertStateReceived(MockedPowerHalService.SET_WAIT_FOR_VHAL, 0);
}
+ /**
+ * Same as {@link #initTest()}, but it also assumes the current and initial users are user 10.
+ */
+ private void initTestForUser10() throws Exception {
+ initTest();
+ setUserInfo(10, NO_USER_INFO_FLAGS);
+ setCurrentUser(10);
+ setInitialUser(10);
+ }
+
@Test
public void testBootComplete() throws Exception {
initTest();
+
+ verifyWtfNeverLogged();
}
@Test
@@ -206,12 +240,13 @@
// display should be turned on as it started with off state.
assertThat(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS)).isTrue();
+
+ verifyWtfNeverLogged();
}
- @Ignore("Disabled until b/147846930 is fixed")
@Test
public void testShutdown() throws Exception {
- initTest();
+ initTestForUser10();
// Transition to ON state
mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, 0));
@@ -227,11 +262,13 @@
assertThat(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS)).isFalse();
mPowerSignalListener.waitForShutdown(WAIT_TIMEOUT_MS);
mSystemStateInterface.waitForShutdown(WAIT_TIMEOUT_MS);
+
+ verifyWtfNeverLogged();
}
@Test
public void testSuspend() throws Exception {
- initTest();
+ initTestForUser10();
// Start in the ON state
mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, 0));
@@ -244,11 +281,13 @@
// Verify suspend
assertStateReceivedForShutdownOrSleepWithPostpone(
PowerHalService.SET_DEEP_SLEEP_ENTRY, WAIT_TIMEOUT_LONG_MS, mWakeupTime);
+
+ verifyWtfNeverLogged();
}
@Test
public void testShutdownOnSuspend() throws Exception {
- initTest();
+ initTestForUser10();
// Start in the ON state
mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, 0));
@@ -280,11 +319,12 @@
// Verify suspend
assertStateReceivedForShutdownOrSleepWithPostpone(
PowerHalService.SET_DEEP_SLEEP_ENTRY, WAIT_TIMEOUT_LONG_MS, mWakeupTime);
+ verifyWtfNeverLogged();
}
@Test
public void testShutdownCancel() throws Exception {
- initTest();
+ initTestForUser10();
// Start in the ON state
mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, 0));
@@ -307,12 +347,12 @@
VehicleApPowerStateShutdownParam.CAN_SLEEP));
assertStateReceivedForShutdownOrSleepWithPostpone(
PowerHalService.SET_DEEP_SLEEP_ENTRY, WAIT_TIMEOUT_LONG_MS, mWakeupTime);
+ verifyWtfNeverLogged();
}
- @Ignore("Disabled until b/147846930 is fixed")
@Test
public void testSleepImmediately() throws Exception {
- initTest();
+ initTestForUser10();
// Transition to ON state
mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, 0));
@@ -328,13 +368,14 @@
assertThat(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS)).isFalse();
mPowerSignalListener.waitForShutdown(WAIT_TIMEOUT_MS);
mSystemStateInterface.waitForShutdown(WAIT_TIMEOUT_MS);
+ verifyWtfNeverLogged();
}
@Test
@WakeupTime(100)
@FlakyTest
public void testShutdownWithProcessing() throws Exception {
- initTest();
+ initTestForUser10();
mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE, 0));
assertStateReceivedForShutdownOrSleepWithPostpone(
PowerHalService.SET_SHUTDOWN_START, WAIT_TIMEOUT_LONG_MS, mWakeupTime);
@@ -342,6 +383,7 @@
// Send the finished signal
mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.FINISHED, 0));
mSystemStateInterface.waitForShutdown(WAIT_TIMEOUT_MS);
+ verifyWtfNeverLogged();
}
@Test
@@ -358,6 +400,7 @@
mSystemStateInterface.waitForSleepEntryAndWakeup(WAIT_TIMEOUT_MS);
assertStateReceived(PowerHalService.SET_DEEP_SLEEP_EXIT, 0);
mPowerSignalListener.waitForSleepExit(WAIT_TIMEOUT_MS);
+ verifyWtfNeverLogged();
}
@Test
@@ -372,6 +415,7 @@
suspendAndResumeForUserSwitchingTests();
verifyUserSwitched(11);
+ verifyWtfNeverLogged();
}
@Test
@@ -384,6 +428,7 @@
suspendAndResumeForUserSwitchingTests();
verifyUserNotSwitched();
+ verifyWtfNeverLogged();
}
@Test
@@ -397,6 +442,7 @@
suspendAndResumeForUserSwitchingTests();
verifyUserSwitched(11);
+ verifyWtfNeverLogged();
}
@Test
@@ -412,6 +458,7 @@
verifyUserRemoved(10);
verifyUserSwitched(11);
+ verifyWtfNeverLogged();
}
@Test
@@ -427,6 +474,7 @@
verifyUserRemoved(11);
verifyUserSwitched(12);
+ verifyWtfNeverLogged();
}
@Test
@@ -442,6 +490,7 @@
verifyUserNotSwitched();
verifyUserNotRemoved(10);
+ // expects WTF
}
@Test
@@ -457,6 +506,7 @@
verifyUserRemoved(11);
verifyUserSwitched(12);
+ verifyWtfNeverLogged();
}
@Test
@@ -471,6 +521,7 @@
verifyUserNotSwitched();
verifyNoGuestCreated();
+ verifyWtfNeverLogged();
}
@Test
@@ -482,6 +533,7 @@
suspendAndResumeForUserSwitchingTests();
verifyUserNotSwitched();
+ // expects WTF
}
@Test
@@ -496,6 +548,7 @@
suspendAndResumeForUserSwitchingTests();
verifyUserNotSwitched();
+ verifyWtfNeverLogged();
}
@Test
@@ -509,6 +562,7 @@
suspendAndResumeForUserSwitchingTests();
verifyUserNotSwitched();
+ verifyWtfNeverLogged();
}
@Test
@@ -523,6 +577,7 @@
suspendAndResumeForUserSwitchingTests();
verifyUserNotSwitched();
+ verifyWtfNeverLogged();
}
@Test
@@ -539,7 +594,7 @@
verifyUserRemoved(10);
verifyUserSwitched(11);
-
+ verifyWtfNeverLogged();
}
@Test
@@ -556,6 +611,7 @@
verifyUserRemoved(11);
verifyUserSwitched(12);
+ verifyWtfNeverLogged();
}
@Test
@@ -573,6 +629,7 @@
verifyUserNotSwitched();
verifyUserNotRemoved(10);
+ // expects WTF
}
@Test
@@ -590,6 +647,7 @@
verifyUserRemoved(11);
verifyUserSwitched(12);
+ verifyWtfNeverLogged();
}
@Test
@@ -605,6 +663,7 @@
verifyUserNotSwitched();
verifyNoGuestCreated();
+ verifyWtfNeverLogged();
}
@Test
@@ -617,6 +676,7 @@
suspendAndResumeForUserSwitchingTests();
verifyUserNotSwitched();
+ // expects WTF
}
private void suspendAndResumeForUserSwitchingTests() throws Exception {
@@ -702,6 +762,23 @@
}
}
+ // TODO: should be part of @After, but then it would hide the real test failure (if any). We'd
+ // need a custom rule (like CTS's SafeCleaner) for it...
+ private void verifyWtfNeverLogged() {
+ int size = mWtfs.size();
+
+ switch (size) {
+ case 0:
+ return;
+ case 1:
+ throw mWtfs.get(0);
+ default:
+ StringBuilder msg = new StringBuilder("wtf called ").append(size).append(" times")
+ .append(": ").append(mWtfs);
+ fail(msg.toString());
+ }
+ }
+
private static void waitForSemaphore(Semaphore semaphore, long timeoutMs)
throws InterruptedException {
if (!semaphore.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
diff --git a/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceControllerTest.java b/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceControllerTest.java
index d7d624c..fd86e09 100644
--- a/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceControllerTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceControllerTest.java
@@ -39,10 +39,10 @@
import android.os.UserManager;
import androidx.test.core.app.ApplicationProvider;
-import androidx.test.filters.FlakyTest;
import com.android.car.CarLocalServices;
import com.android.car.user.CarUserService;
+import com.android.internal.annotations.GuardedBy;
import org.junit.After;
import org.junit.Before;
@@ -138,28 +138,27 @@
}
@Test
- @FlakyTest
public void systemUserUnlocked() {
mController.init();
mContext.reset();
// Unlock system user
mockUserUnlock(UserHandle.USER_SYSTEM, true);
- runOnMainThread(() -> mCarUserService.setUserLockStatus(UserHandle.USER_SYSTEM, true));
+ runOnMainThreadAndWaitForIdle(() -> mCarUserService.setUserLockStatus(
+ UserHandle.USER_SYSTEM, true));
mContext.assertStartedService(SERVICE_START_SYSTEM_UNLOCKED);
mContext.verifyNoMoreServiceLaunches();
}
@Test
- @FlakyTest
public void fgUserUnlocked() {
mController.init();
mContext.reset();
// Switch user to foreground
when(ActivityManager.getCurrentUser()).thenReturn(FG_USER_ID);
- runOnMainThread(() -> mCarUserService.onSwitchUser(FG_USER_ID));
+ runOnMainThreadAndWaitForIdle(() -> mCarUserService.onSwitchUser(FG_USER_ID));
// Expect only services with ASAP trigger to be started
mContext.assertBoundService(SERVICE_BIND_ALL_USERS_ASAP);
@@ -167,14 +166,16 @@
// Unlock foreground user
mockUserUnlock(FG_USER_ID, true);
- runOnMainThread(() -> mCarUserService.setUserLockStatus(FG_USER_ID, true));
+ runOnMainThreadAndWaitForIdle(() -> mCarUserService.setUserLockStatus(FG_USER_ID, true));
mContext.assertBoundService(SERVICE_BIND_FG_USER_UNLOCKED);
mContext.verifyNoMoreServiceLaunches();
}
- private void runOnMainThread(Runnable r) {
+ private void runOnMainThreadAndWaitForIdle(Runnable r) {
Handler.getMain().runWithScissors(r, DEFAULT_TIMEOUT_MS);
+ // Run empty runnable to make sure that all posted handlers are done.
+ Handler.getMain().runWithScissors(() -> { }, DEFAULT_TIMEOUT_MS);
}
private void mockUserUnlock(int userId, boolean unlock) {
@@ -189,7 +190,11 @@
/** Overrides framework behavior to succeed on binding/starting processes. */
public class ServiceLauncherContext extends ContextWrapper {
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
private List<Intent> mBoundIntents = new ArrayList<>();
+ @GuardedBy("mLock")
private List<Intent> mStartedServicesIntents = new ArrayList<>();
ServiceLauncherContext(Context base) {
@@ -198,14 +203,18 @@
@Override
public ComponentName startServiceAsUser(Intent service, UserHandle user) {
- mStartedServicesIntents.add(service);
+ synchronized (mLock) {
+ mStartedServicesIntents.add(service);
+ }
return service.getComponent();
}
@Override
public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
Handler handler, UserHandle user) {
- mBoundIntents.add(service);
+ synchronized (mLock) {
+ mBoundIntents.add(service);
+ }
conn.onServiceConnected(service.getComponent(), null);
return true;
}
@@ -222,27 +231,35 @@
}
void assertBoundService(String service) {
- assertThat(mBoundIntents).hasSize(1);
- assertThat(mBoundIntents.get(0).getComponent())
- .isEqualTo(ComponentName.unflattenFromString(service));
- mBoundIntents.clear();
+ synchronized (mLock) {
+ assertThat(mBoundIntents).hasSize(1);
+ assertThat(mBoundIntents.get(0).getComponent())
+ .isEqualTo(ComponentName.unflattenFromString(service));
+ mBoundIntents.clear();
+ }
}
void assertStartedService(String service) {
- assertThat(mStartedServicesIntents).hasSize(1);
- assertThat(mStartedServicesIntents.get(0).getComponent())
- .isEqualTo(ComponentName.unflattenFromString(service));
- mStartedServicesIntents.clear();
+ synchronized (mLock) {
+ assertThat(mStartedServicesIntents).hasSize(1);
+ assertThat(mStartedServicesIntents.get(0).getComponent())
+ .isEqualTo(ComponentName.unflattenFromString(service));
+ mStartedServicesIntents.clear();
+ }
}
void verifyNoMoreServiceLaunches() {
- assertThat(mStartedServicesIntents).isEmpty();
- assertThat(mBoundIntents).isEmpty();
+ synchronized (mLock) {
+ assertThat(mStartedServicesIntents).isEmpty();
+ assertThat(mBoundIntents).isEmpty();
+ }
}
void reset() {
- mStartedServicesIntents.clear();
- mBoundIntents.clear();
+ synchronized (mLock) {
+ mStartedServicesIntents.clear();
+ mBoundIntents.clear();
+ }
}
diff --git a/tests/carservice_unit_test/src/com/android/car/user/CarUserNoticeServiceTest.java b/tests/carservice_unit_test/src/com/android/car/user/CarUserNoticeServiceTest.java
index 3c434f1..93d9500 100644
--- a/tests/carservice_unit_test/src/com/android/car/user/CarUserNoticeServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/user/CarUserNoticeServiceTest.java
@@ -41,6 +41,8 @@
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Looper;
import android.os.PowerManager;
import android.os.UserHandle;
import android.provider.Settings;
@@ -97,6 +99,8 @@
private MockitoSession mSession;
private CarUserNoticeService mCarUserNoticeService;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+
/**
* Initialize all of the objects with the @Mock annotation.
*/
@@ -126,8 +130,7 @@
doReturn(mMockAppOpsManager).when(mMockContext).getSystemService(AppOpsManager.class);
doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
doReturn(1).when(mMockPackageManager).getPackageUidAsUser(any(), anyInt());
-
- mCarUserNoticeService = new CarUserNoticeService(mMockContext);
+ mCarUserNoticeService = new CarUserNoticeService(mMockContext, mHandler);
mCarUserNoticeService.init();
verify(mMockCarUserService).addUserCallback(mUserCallback.capture());
verify(mMockContext).registerReceiver(mDisplayBroadcastReceiver.capture(),
@@ -274,7 +277,7 @@
private void sendBroadcast(String action) {
Intent intent = new Intent();
intent.setAction(action);
- mDisplayBroadcastReceiver.getValue().onReceive(mMockContext, intent);
+ mHandler.post(() -> mDisplayBroadcastReceiver.getValue().onReceive(mMockContext, intent));
}
private void sendPowerStateChange(int state) {