overlay UI for pull down volume control am: 077137c7a3
am: a85b73e899
Change-Id: I810b24be968b2d4c0a0c54cc3d04b2816f83161f
diff --git a/car-lib/api/system-current.txt b/car-lib/api/system-current.txt
index 52c96d2..acb24a7 100644
--- a/car-lib/api/system-current.txt
+++ b/car-lib/api/system-current.txt
@@ -485,6 +485,7 @@
method public boolean isFreezeFrameNotificationSupported() throws android.car.CarNotConnectedException;
method public boolean isGetFreezeFrameSupported() throws android.car.CarNotConnectedException;
method public boolean isLiveFrameSupported() throws android.car.CarNotConnectedException;
+ method public boolean isSelectiveClearFreezeFramesSupported() throws android.car.CarNotConnectedException;
method public void onCarDisconnected();
method public boolean registerListener(android.car.diagnostic.CarDiagnosticManager.OnDiagnosticEventListener, int, int) throws android.car.CarNotConnectedException, java.lang.IllegalArgumentException;
method public void unregisterListener(android.car.diagnostic.CarDiagnosticManager.OnDiagnosticEventListener);
diff --git a/car-lib/src/android/car/diagnostic/CarDiagnosticManager.java b/car-lib/src/android/car/diagnostic/CarDiagnosticManager.java
index 84cdd3e..88980bf 100644
--- a/car-lib/src/android/car/diagnostic/CarDiagnosticManager.java
+++ b/car-lib/src/android/car/diagnostic/CarDiagnosticManager.java
@@ -349,8 +349,13 @@
}
/**
- * Returns true if this vehicle supports clearing freeze frame timestamps.
+ * Returns true if this vehicle supports clearing all freeze frames.
* This is only meaningful if freeze frame data is also supported.
+ *
+ * A return value of true for this method indicates that it is supported to call
+ * carDiagnosticManager.clearFreezeFrames()
+ * to delete all freeze frames stored in vehicle memory.
+ *
* @return
* @throws CarNotConnectedException
*/
@@ -365,6 +370,28 @@
return false;
}
+ /**
+ * Returns true if this vehicle supports clearing specific freeze frames by timestamp.
+ * This is only meaningful if freeze frame data is also supported.
+ *
+ * A return value of true for this method indicates that it is supported to call
+ * carDiagnosticManager.clearFreezeFrames(timestamp1, timestamp2, ...)
+ * to delete the freeze frames stored for the provided input timestamps, provided any exist.
+ *
+ * @return
+ * @throws CarNotConnectedException
+ */
+ public boolean isSelectiveClearFreezeFramesSupported() throws CarNotConnectedException {
+ try {
+ return mService.isSelectiveClearFreezeFramesSupported();
+ } catch (IllegalStateException e) {
+ CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException();
+ }
+ return false;
+ }
+
private static class CarDiagnosticEventListenerToService
extends Stub {
private final WeakReference<CarDiagnosticManager> mManager;
diff --git a/car-lib/src/android/car/diagnostic/ICarDiagnostic.aidl b/car-lib/src/android/car/diagnostic/ICarDiagnostic.aidl
index 3d1808f..57443d8 100644
--- a/car-lib/src/android/car/diagnostic/ICarDiagnostic.aidl
+++ b/car-lib/src/android/car/diagnostic/ICarDiagnostic.aidl
@@ -74,4 +74,10 @@
* Returns whether the underlying HAL supports clearing freeze frames.
*/
boolean isClearFreezeFramesSupported() = 10;
+
+ /**
+ * Returns whether the underlying HAL supports clearing specific freeze frames specified
+ * by means of their timestamps.
+ */
+ boolean isSelectiveClearFreezeFramesSupported() = 11;
}
diff --git a/car-support-lib/proguard-extra-keeps.flags b/car-support-lib/proguard-extra-keeps.flags
index 5e67c6e..72ea927 100644
--- a/car-support-lib/proguard-extra-keeps.flags
+++ b/car-support-lib/proguard-extra-keeps.flags
@@ -17,3 +17,7 @@
public static ... createCluster(...);
public static ... createCustomImageCluster(...);
}
+
+-keep class android.support.car.CarManagerBase {
+ *;
+}
diff --git a/car-support-lib/src/android/support/car/Car.java b/car-support-lib/src/android/support/car/Car.java
index a374d87..e0e3a0d 100644
--- a/car-support-lib/src/android/support/car/Car.java
+++ b/car-support-lib/src/android/support/car/Car.java
@@ -237,6 +237,7 @@
if (mConnectionState == STATE_DISCONNECTED) {
return;
}
+ tearDownCarManagers();
mConnectionState = STATE_DISCONNECTED;
}
mCarConnectionCallback.onDisconnected(Car.this);
diff --git a/service/src/com/android/car/CarDiagnosticService.java b/service/src/com/android/car/CarDiagnosticService.java
index 809439c..3bd9979 100644
--- a/service/src/com/android/car/CarDiagnosticService.java
+++ b/service/src/com/android/car/CarDiagnosticService.java
@@ -411,6 +411,13 @@
diagnosticCapabilities.isFreezeFrameSupported();
}
+ public boolean isSelectiveClearFreezeFramesSupported() {
+ DiagnosticCapabilities diagnosticCapabilities =
+ getDiagnosticHal().getDiagnosticCapabilities();
+ return isClearFreezeFramesSupported() &&
+ diagnosticCapabilities.isSelectiveClearFreezeFramesSupported();
+ }
+
// ICarDiagnostic implementations
@Override
@@ -441,14 +448,18 @@
@Override
public boolean clearFreezeFrames(long... timestamps) {
mDiagnosticClearPermission.assertGranted();
- if (mDiagnosticHal.getDiagnosticCapabilities().isFreezeFrameClearSupported()) {
- mFreezeFrameDiagnosticRecords.lock();
- mDiagnosticHal.clearFreezeFrames(timestamps);
- mFreezeFrameDiagnosticRecords.clearEvents();
- mFreezeFrameDiagnosticRecords.unlock();
- return true;
+ if (!isClearFreezeFramesSupported())
+ return false;
+ if (timestamps != null && timestamps.length != 0) {
+ if (!isSelectiveClearFreezeFramesSupported()) {
+ return false;
+ }
}
- return false;
+ mFreezeFrameDiagnosticRecords.lock();
+ mDiagnosticHal.clearFreezeFrames(timestamps);
+ mFreezeFrameDiagnosticRecords.clearEvents();
+ mFreezeFrameDiagnosticRecords.unlock();
+ return true;
}
/**
diff --git a/service/src/com/android/car/CarVolumeControllerFactory.java b/service/src/com/android/car/CarVolumeControllerFactory.java
index 4e9bd4f..96d1e14 100644
--- a/service/src/com/android/car/CarVolumeControllerFactory.java
+++ b/service/src/com/android/car/CarVolumeControllerFactory.java
@@ -17,11 +17,13 @@
package com.android.car;
import android.content.Context;
+import android.hardware.automotive.vehicle.V2_0.VehicleAudioContextFlag;
import android.media.AudioManager;
import android.media.IAudioService;
import android.media.IVolumeController;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteCallbackList;
@@ -89,8 +91,21 @@
* support volume controls.
*/
public static final class SimpleCarVolumeController extends CarVolumeController {
+ private static final String TAG = CarLog.TAG_AUDIO + ".SVolCtrl";
+ private static final int MSG_DISPLAY_SAFE_VOL_WARNING = 0;
+ private static final int MSG_VOL_CHANGED = 1;
+ private static final int MSG_MASTER_MUTE_CHANGED = 2;
+ private static final int MSG_SET_LAYOUT_DIRECTION = 3;
+ private static final int MSG_DISMISS = 4;
+ private static final int MSG_SET_ALLY_MODE = 5;
+
private final AudioManager mAudioManager;
private final Context mContext;
+ private RemoteCallbackList<IVolumeController> mVolumeCallbacks = new RemoteCallbackList<>();
+ @GuardedBy("this")
+ private IVolumeController mVolumeCallbackProxy;
+ private HandlerThread mVolumeThread;
+ private Handler mHandler;
public SimpleCarVolumeController(Context context) {
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
@@ -99,16 +114,27 @@
@Override
void init() {
+ synchronized (this) {
+ mVolumeThread = new HandlerThread(TAG);
+ mVolumeThread.start();
+ mHandler = new VolumeHandler(mVolumeThread.getLooper());
+ }
}
@Override
void release() {
+ synchronized (this) {
+ if (mVolumeThread != null) {
+ mVolumeThread.quit();
+ }
+ }
+ mVolumeCallbacks.kill();
}
@Override
public void setStreamVolume(int stream, int index, int flags) {
if (DBG) {
- Log.d(CarLog.TAG_AUDIO, "setStreamVolume " + stream + " " + index + " " + flags);
+ Log.d(TAG, "setStreamVolume " + stream + " " + index + " " + flags);
}
mAudioManager.setStreamVolume(stream, index, flags);
}
@@ -120,7 +146,13 @@
@Override
public void setVolumeController(IVolumeController controller) {
- mAudioManager.setVolumeController(controller);
+ synchronized (this) {
+ if (mVolumeCallbackProxy == null) {
+ mVolumeCallbackProxy = new VolumeControllerProxy();
+ mAudioManager.setVolumeController(mVolumeCallbackProxy);
+ }
+ }
+ mVolumeCallbacks.register(controller);
}
@Override
@@ -148,6 +180,146 @@
// nothing else to dump
}
+ private class VolumeHandler extends Handler {
+
+ public VolumeHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_DISPLAY_SAFE_VOL_WARNING:
+ int count = mVolumeCallbacks.beginBroadcast();
+ try {
+ for (int i = 0; i < count; i++) {
+ try {
+ mVolumeCallbacks.getBroadcastItem(i)
+ .displaySafeVolumeWarning(msg.arg1);
+ } catch (RemoteException ignored) {}
+ }
+ } finally {
+ mVolumeCallbacks.finishBroadcast();
+ }
+ break;
+ case MSG_VOL_CHANGED:
+ count = mVolumeCallbacks.beginBroadcast();
+ try {
+ for (int i = 0; i < count; i++) {
+ try {
+ mVolumeCallbacks.getBroadcastItem(i)
+ .volumeChanged(msg.arg1, msg.arg2);
+ } catch (RemoteException ignored) {}
+ }
+ } finally {
+ mVolumeCallbacks.finishBroadcast();
+ }
+ break;
+ case MSG_MASTER_MUTE_CHANGED:
+ count = mVolumeCallbacks.beginBroadcast();
+ try {
+ for (int i = 0; i < count; i++) {
+ try {
+ mVolumeCallbacks.getBroadcastItem(i)
+ .masterMuteChanged(msg.arg1);
+ } catch (RemoteException ignored) {}
+ }
+ } finally {
+ mVolumeCallbacks.finishBroadcast();
+ }
+ break;
+ case MSG_SET_LAYOUT_DIRECTION:
+ count = mVolumeCallbacks.beginBroadcast();
+ try {
+ for (int i = 0; i < count; i++) {
+ try {
+ mVolumeCallbacks.getBroadcastItem(i)
+ .setLayoutDirection(msg.arg1);
+ } catch (RemoteException ignored) {}
+ }
+ } finally {
+ mVolumeCallbacks.finishBroadcast();
+ }
+ break;
+ case MSG_DISMISS:
+ count = mVolumeCallbacks.beginBroadcast();
+ try {
+ for (int i = 0; i < count; i++) {
+ try {
+ mVolumeCallbacks.getBroadcastItem(i)
+ .dismiss();
+ } catch (RemoteException ignored) {}
+ }
+ } finally {
+ mVolumeCallbacks.finishBroadcast();
+ }
+ break;
+ case MSG_SET_ALLY_MODE:
+ count = mVolumeCallbacks.beginBroadcast();
+ try {
+ for (int i = 0; i < count; i++) {
+ try {
+ mVolumeCallbacks.getBroadcastItem(i)
+ .setA11yMode(msg.arg1);
+ } catch (RemoteException ignored) {}
+ }
+ } finally {
+ mVolumeCallbacks.finishBroadcast();
+ }
+ break;
+ }
+ }
+ }
+
+ private class VolumeControllerProxy extends IVolumeController.Stub {
+ @Override
+ public void displaySafeVolumeWarning(int flags) throws RemoteException {
+ synchronized (this) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPLAY_SAFE_VOL_WARNING,
+ flags, 0 /*unused*/));
+ }
+ }
+
+ @Override
+ public void volumeChanged(int streamType, int flags) throws RemoteException {
+ synchronized (this) {
+ mHandler.sendMessage(
+ mHandler.obtainMessage(MSG_VOL_CHANGED, streamType, flags));
+ }
+ }
+
+ @Override
+ public void masterMuteChanged(int flags) throws RemoteException {
+ synchronized (this) {
+ mHandler.sendMessage(
+ mHandler.obtainMessage(MSG_VOL_CHANGED, flags, 0 /*unused*/));
+ }
+ }
+
+ @Override
+ public void setLayoutDirection(int layoutDirection) throws RemoteException {
+ synchronized (this) {
+ mHandler.sendMessage(
+ mHandler.obtainMessage(MSG_VOL_CHANGED, layoutDirection, 0 /*unused*/));
+ }
+ }
+
+ @Override
+ public void dismiss() throws RemoteException {
+ synchronized (this) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_DISMISS));
+ }
+ }
+
+ @Override
+ public void setA11yMode(int mode) throws RemoteException {
+ synchronized (this) {
+ mHandler.sendMessage(
+ mHandler.obtainMessage(MSG_SET_ALLY_MODE, mode, 0 /*unused*/));
+ }
+ }
+ }
+
private void handleVolumeKeyDefault(KeyEvent event) {
if (event.getAction() != KeyEvent.ACTION_DOWN
|| interceptVolKeyBeforeDispatching(mContext)) {
@@ -254,7 +426,8 @@
AudioHalService.carContextToCarUsage(carContext));
return physicalStream;
} else {
- return carContext;
+ return carContext == VehicleAudioContextFlag.UNKNOWN_FLAG ?
+ mCurrentContext : carContext;
}
}
@@ -533,7 +706,7 @@
synchronized (this) {
int flag = getVolumeUpdateFlag(true);
if (DBG) {
- Log.d(TAG, "onVolumeChange carStream:" + carStream + " volume: " + volume
+ Log.d(TAG, "onVolumeChange carStream: " + carStream + " volume: " + volume
+ " volumeState: " + volumeState
+ " suppressUI? " + mShouldSuppress
+ " stream: " + mSuppressUiForVolume[0]
@@ -543,6 +716,13 @@
if (mMasterVolumeOnly) { //for master volume only H/W, always assume current stream
carStream = currentCarStream;
}
+
+ // Map the UNKNOWN context to the current context.
+ if (mSupportedAudioContext != 0
+ && carStream == VehicleAudioContextFlag.UNKNOWN_FLAG) {
+ carStream = mCurrentContext;
+ }
+
if (currentCarStream == carStream) {
mCurrentCarContextVolume.put(mCurrentContext, volume);
writeVolumeToSettings(mCurrentContext, volume);
diff --git a/service/src/com/android/car/hal/DiagnosticHalService.java b/service/src/com/android/car/hal/DiagnosticHalService.java
index 4ff0ada..bcbae7a 100644
--- a/service/src/com/android/car/hal/DiagnosticHalService.java
+++ b/service/src/com/android/car/hal/DiagnosticHalService.java
@@ -42,6 +42,8 @@
* higher-level semantic information
*/
public class DiagnosticHalService extends SensorHalServiceBase {
+ static final int OBD2_SELECTIVE_FRAME_CLEAR = 1;
+
public static class DiagnosticCapabilities {
private final CopyOnWriteArraySet<Integer> mProperties = new CopyOnWriteArraySet<>();
@@ -69,6 +71,10 @@
return isSupported(VehicleProperty.OBD2_FREEZE_FRAME_CLEAR);
}
+ public boolean isSelectiveClearFreezeFramesSupported() {
+ return isSupported(OBD2_SELECTIVE_FRAME_CLEAR);
+ }
+
void clear() {
mProperties.clear();
}
@@ -102,6 +108,17 @@
return propConfig.prop;
case VehicleProperty.OBD2_FREEZE_FRAME_CLEAR:
mDiagnosticCapabilities.setSupported(propConfig.prop);
+ Log.i(CarLog.TAG_DIAGNOSTIC, String.format(
+ "configArray for OBD2_FREEZE_FRAME_CLEAR is %s", propConfig.configArray));
+ if (propConfig.configArray.size() < 1) {
+ Log.e(CarLog.TAG_DIAGNOSTIC, String.format(
+ "property 0x%x does not specify whether it supports selective " +
+ "clearing of freeze frames. assuming it does not.", propConfig.prop));
+ } else {
+ if (propConfig.configArray.get(0) == 1) {
+ mDiagnosticCapabilities.setSupported(OBD2_SELECTIVE_FRAME_CLEAR);
+ }
+ }
return propConfig.prop;
default:
return SENSOR_TYPE_INVALID;
diff --git a/tests/EmbeddedKitchenSinkApp/res/raw/john_harrison_with_the_wichita_state_university_chamber_players_05_summer_mvt_2_adagio.mp3 b/tests/EmbeddedKitchenSinkApp/res/raw/john_harrison_with_the_wichita_state_university_chamber_players_05_summer_mvt_2_adagio.mp3
deleted file mode 100644
index 25e6be6..0000000
--- a/tests/EmbeddedKitchenSinkApp/res/raw/john_harrison_with_the_wichita_state_university_chamber_players_05_summer_mvt_2_adagio.mp3
+++ /dev/null
Binary files differ
diff --git a/tests/EmbeddedKitchenSinkApp/res/raw/well_worth_the_wait.mp3 b/tests/EmbeddedKitchenSinkApp/res/raw/well_worth_the_wait.mp3
new file mode 100644
index 0000000..a1db0bc
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/raw/well_worth_the_wait.mp3
Binary files differ
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
index 81e8737..ce90651 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
@@ -175,7 +175,7 @@
//ignore for now
}
- mMusicPlayer = new AudioPlayer(mContext, R.raw.john_harrison_with_the_wichita_state_university_chamber_players_05_summer_mvt_2_adagio,
+ mMusicPlayer = new AudioPlayer(mContext, R.raw.well_worth_the_wait,
mMusicAudioAttrib);
mMusicPlayerShort = new AudioPlayer(mContext, R.raw.ring_classic_01,
mMusicAudioAttrib);
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/volume/VolumeTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/volume/VolumeTestFragment.java
index 0d65500..874bc64 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/volume/VolumeTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/volume/VolumeTestFragment.java
@@ -18,11 +18,14 @@
import android.car.Car;
import android.car.CarNotConnectedException;
import android.car.media.CarAudioManager;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.ServiceConnection;
import android.media.AudioManager;
import android.media.IVolumeController;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.support.annotation.Nullable;
@@ -36,10 +39,9 @@
import android.widget.Button;
import android.widget.ListView;
-import com.google.android.car.kitchensink.CarEmulator;
import com.google.android.car.kitchensink.R;
-public class VolumeTestFragment extends Fragment{
+public class VolumeTestFragment extends Fragment {
private static final String TAG = "CarVolumeTest";
private static final int MSG_VOLUME_CHANGED = 0;
private static final int MSG_REQUEST_FOCUS = 1;
@@ -52,7 +54,6 @@
private CarAudioManager mCarAudioManager;
private Car mCar;
- private CarEmulator mCarEmulator;
private Button mVolumeUp;
private Button mVolumeDown;
@@ -140,6 +141,26 @@
default: return "Unknown";
}
}
+ private final ServiceConnection mCarConnectionCallback =
+ new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder binder) {
+ Log.d(TAG, "Connected to Car Service");
+ try {
+ mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
+ initVolumeInfo();
+ mCarAudioManager.setVolumeController(mVolumeController);
+
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.d(TAG, "Disconnect from Car Service");
+ }
+ };
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -160,33 +181,8 @@
}
});
- mCarEmulator = CarEmulator.create(getContext());
- mCar = mCarEmulator.getCar();
- try {
- mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
- initVolumeInfo();
- mCarAudioManager.setVolumeController(mVolumeController);
- } catch (CarNotConnectedException e) {
- throw new RuntimeException(e); // Should never occur in car emulator.
- }
-
- mVolumeUp = (Button) v.findViewById(R.id.volume_up);
- mVolumeDown = (Button) v.findViewById(R.id.volume_down);
-
- mVolumeUp.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mCarEmulator.injectKey(KeyEvent.KEYCODE_VOLUME_UP);
- }
- });
-
- mVolumeDown.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mCarEmulator.injectKey(KeyEvent.KEYCODE_VOLUME_DOWN);
- }
- });
-
+ mCar = Car.createCar(getActivity(), mCarConnectionCallback);
+ mCar.connect();
return v;
}
@@ -214,7 +210,8 @@
return;
}
try {
- mCarAudioManager.setStreamVolume(logicalStream, volume, 0);
+ mCarAudioManager.setStreamVolume(logicalStream, volume,
+ AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_PLAY_SOUND);
} catch (CarNotConnectedException e) {
Log.e(TAG, "car not connected", e);
}
diff --git a/tests/carservice_test/Android.mk b/tests/carservice_test/Android.mk
index 4be5354..5c668af 100644
--- a/tests/carservice_test/Android.mk
+++ b/tests/carservice_test/Android.mk
@@ -41,7 +41,7 @@
vehicle-hal-support-lib \
car-systemtest \
android-support-test \
- android.hardware.automotive.vehicle-V2.0-java-static
+ android.hardware.automotive.vehicle-V2.0-java
LOCAL_JAVA_LIBRARIES := android.car android.test.runner
diff --git a/tests/carservice_test/src/com/android/car/test/CarDiagnosticManagerTest.java b/tests/carservice_test/src/com/android/car/test/CarDiagnosticManagerTest.java
index b77a844..56d8a00 100644
--- a/tests/carservice_test/src/com/android/car/test/CarDiagnosticManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarDiagnosticManagerTest.java
@@ -43,6 +43,7 @@
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
@@ -190,6 +191,7 @@
@Override
protected synchronized void configureMockedHal() {
java.util.Collection<Integer> numVendorSensors = Arrays.asList(0, 0);
+ java.util.Collection<Integer> selectiveClear = Collections.singletonList(1);
addProperty(VehicleProperty.OBD2_LIVE_FRAME, mLiveFrameEventBuilder.build())
.setConfigArray(numVendorSensors);
addProperty(
@@ -199,7 +201,8 @@
.setConfigArray(numVendorSensors);
addProperty(
VehicleProperty.OBD2_FREEZE_FRAME_CLEAR,
- mFreezeFrameProperties.mFreezeFrameClearHandler);
+ mFreezeFrameProperties.mFreezeFrameClearHandler)
+ .setConfigArray(selectiveClear);
}
@Override
@@ -732,6 +735,7 @@
assertTrue(mCarDiagnosticManager.isFreezeFrameNotificationSupported());
assertTrue(mCarDiagnosticManager.isGetFreezeFrameSupported());
assertTrue(mCarDiagnosticManager.isClearFreezeFramesSupported());
+ assertTrue(mCarDiagnosticManager.isSelectiveClearFreezeFramesSupported());
}
class Listener implements CarDiagnosticManager.OnDiagnosticEventListener {
diff --git a/tests/carservice_test/src/com/android/car/test/CarVolumeServiceTest.java b/tests/carservice_test/src/com/android/car/test/CarVolumeServiceTest.java
index d05326b..cd51e01 100644
--- a/tests/carservice_test/src/com/android/car/test/CarVolumeServiceTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarVolumeServiceTest.java
@@ -17,12 +17,14 @@
import static com.android.car.test.AudioTestUtils.doRequestFocus;
+import com.android.car.VolumeUtils;
import com.google.android.collect.Lists;
import android.car.Car;
import android.car.CarNotConnectedException;
import android.car.media.CarAudioManager;
import android.content.Context;
+import android.hardware.automotive.vehicle.V2_0.VehicleAudioContextFlag;
import android.hardware.automotive.vehicle.V2_0.VehicleAudioExtFocusFlag;
import android.hardware.automotive.vehicle.V2_0.VehicleAudioFocusState;
import android.hardware.automotive.vehicle.V2_0.VehicleAudioVolumeIndex;
@@ -81,17 +83,57 @@
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
}
});
+ }
+ private SingleChannelVolumeHandler setupExternalVolumeEmulation(boolean supportAudioContext)
+ throws Exception {
List<Integer> maxs = new ArrayList<>();
- maxs.add(MAX_VOL);
- maxs.add(MAX_VOL);
-
- // TODO: add tests for audio context supported cases.
- startVolumeEmulation(0 /*supported audio context*/, maxs);
+ int supportedAudioContext = 0;
+ if (!supportAudioContext) {
+ // set up 2 physical streams
+ maxs.add(MAX_VOL);
+ maxs.add(MAX_VOL);
+ } else {
+ // add supported contexts
+ int[] contexts = VolumeUtils.CAR_AUDIO_CONTEXT;
+ for (int context : contexts) {
+ supportedAudioContext |= context;
+ maxs.add(MAX_VOL);
+ }
+ }
+ SingleChannelVolumeHandler handler =
+ startVolumeEmulation(true, supportedAudioContext, maxs);
mCarAudioManager = (CarAudioManager) getCar().getCarManager(Car.AUDIO_SERVICE);
+ return handler;
+ }
+
+ public void testUnknownVolumeChange() throws Exception {
+ SingleChannelVolumeHandler volumeHandler = setupExternalVolumeEmulation(true);
+ VolumeController volumeController = new VolumeController();
+ mCarAudioManager.setVolumeController(volumeController);
+ mCarAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 2, 0);
+ // give focus to music, now current context becomes VehicleAudioContextFlag.MUSIC_FLAG
+ CarAudioFocusTest.AudioFocusListener listenerMusic =
+ new CarAudioFocusTest.AudioFocusListener();
+ int res = doRequestFocus(mAudioManager, listenerMusic,
+ AudioManager.STREAM_MUSIC,
+ AudioManager.AUDIOFOCUS_GAIN);
+ assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
+ int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.STATE_GAIN,
+ request[1],
+ VehicleAudioExtFocusFlag.NONE_FLAG);
+
+ // let vehicle hal report volume change from unknown context, we should map it to the
+ // current context (music).
+ volumeHandler.injectVolumeEvent(VehicleAudioContextFlag.UNKNOWN_FLAG, 3);
+ // now music volume should be recorded as 3.
+ volumeVerificationPoll(createStreamVolPair(AudioManager.STREAM_MUSIC, 3));
}
public void testVolumeLimits() throws Exception {
+ setupExternalVolumeEmulation(false);
for (int stream : LOGICAL_STREAMS) {
assertEquals(MAX_VOL, mCarAudioManager.getStreamMaxVolume(stream));
}
@@ -99,6 +141,7 @@
public void testVolumeSet() {
try {
+ setupExternalVolumeEmulation(false);
int callVol = 10;
int musicVol = 15;
mCarAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, musicVol, 0);
@@ -112,13 +155,33 @@
volumeVerificationPoll(createStreamVolPair(AudioManager.STREAM_MUSIC, MAX_VOL),
createStreamVolPair(AudioManager.STREAM_VOICE_CALL, callVol));
- } catch (CarNotConnectedException e) {
- fail("Car not connected");
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+ }
+
+ public void testMultipleVolumeControllers() {
+ try {
+ startVolumeEmulation(false, 0, new ArrayList<>());
+ mCarAudioManager = (CarAudioManager) getCar().getCarManager(Car.AUDIO_SERVICE);
+ mCarAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0);
+ VolumeController volumeController1 = new VolumeController();
+ VolumeController volumeController2 = new VolumeController();
+ mCarAudioManager.setVolumeController(volumeController1);
+ mCarAudioManager.setVolumeController(volumeController2);
+
+ mCarAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
+ 1, AudioManager.FLAG_SHOW_UI);
+ volumeChangeVerificationPoll(AudioManager.STREAM_MUSIC, true, volumeController1);
+ volumeChangeVerificationPoll(AudioManager.STREAM_MUSIC, true, volumeController2);
+ } catch (Exception e) {
+ fail(e.getMessage());
}
}
public void testSuppressVolumeUI() {
try {
+ setupExternalVolumeEmulation(false);
VolumeController volumeController = new VolumeController();
mCarAudioManager.setVolumeController(volumeController);
@@ -164,8 +227,9 @@
}
}
- public void testVolumeKeys() throws Exception {
+ public void testVolumeKeys() {
try {
+ setupExternalVolumeEmulation(false);
int musicVol = 10;
mCarAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, musicVol, 0);
int callVol = 12;
@@ -206,7 +270,7 @@
callVol++;
volumeVerificationPoll(createStreamVolPair(AudioManager.STREAM_MUSIC, musicVol),
createStreamVolPair(AudioManager.STREAM_VOICE_CALL, callVol));
- } catch (CarNotConnectedException | InterruptedException e) {
+ } catch (Exception e) {
fail(e.toString());
}
}
@@ -272,12 +336,21 @@
}
}
+ public void injectVolumeEvent(int context, int volume) {
+ getMockedVehicleHal().injectEvent(
+ VehiclePropValueBuilder.newBuilder(VehicleProperty.AUDIO_VOLUME)
+ .setTimestamp(SystemClock.elapsedRealtimeNanos())
+ .addIntValue(context, volume, 0)
+ .build());
+ }
+
@Override
public void onPropertySet(VehiclePropValue value) {
ArrayList<Integer> v = value.value.int32Values;
int stream = v.get(VehicleAudioVolumeIndex.INDEX_STREAM);
int volume = v.get(VehicleAudioVolumeIndex.INDEX_VOLUME);
int state = v.get(VehicleAudioVolumeIndex.INDEX_STATE);
+ Log.d(TAG, "state " + state);
mCurrent.put(stream, volume);
getMockedVehicleHal().injectEvent(
@@ -358,7 +431,8 @@
}
};
- private void startVolumeEmulation(int supportedAudioVolumeContext, List<Integer> maxs) {
+ private SingleChannelVolumeHandler startVolumeEmulation(boolean supportExternalVolume,
+ int supportedAudioVolumeContext, List<Integer> maxs) {
SingleChannelVolumeHandler singleChannelVolumeHandler =
new SingleChannelVolumeHandler(maxs);
int zones = (1<<maxs.size()) - 1;
@@ -371,9 +445,11 @@
0 /* reserved */);
audioVolumeConfigArray.addAll(maxs);
- addProperty(VehicleProperty.AUDIO_VOLUME, singleChannelVolumeHandler)
- .setConfigArray(audioVolumeConfigArray)
- .setSupportedAreas(zones);
+ if (supportExternalVolume) {
+ addProperty(VehicleProperty.AUDIO_VOLUME, singleChannelVolumeHandler)
+ .setConfigArray(audioVolumeConfigArray)
+ .setSupportedAreas(zones);
+ }
addProperty(VehicleProperty.HW_KEY_INPUT, mHWKeyHandler)
.setAccess(VehiclePropertyAccess.READ);
@@ -390,6 +466,7 @@
.setConfigArray(Lists.newArrayList(0));
reinitializeMockedHal();
+ return singleChannelVolumeHandler;
}
private void sendVolumeKey(boolean volUp) {
diff --git a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Utils.java b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Utils.java
index 5221907..fb97e73 100644
--- a/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Utils.java
+++ b/tests/vehiclehal_test/src/com/android/car/vehiclehal/test/Utils.java
@@ -81,14 +81,12 @@
return readVhalProperty(vehicle, request, f);
}
- @Nullable
static IVehicle getVehicle() throws RemoteException {
IVehicle service;
try {
- service = android.hardware.automotive.vehicle.V2_0.IVehicle.getService();
+ service = IVehicle.getService();
} catch (NoSuchElementException ex) {
- Log.d(TAG, "Couldn't connect to vehicle@2.1, connecting to vehicle@2.0...");
- service = IVehicle.getService();
+ throw new RuntimeException("Couldn't connect to vehicle@2.0", ex);
}
Log.d(TAG, "Connected to IVehicle service: " + service);
return service;