Merge "Added DisplayPowerProximityStateController tests"

This commit is contained in:
Rupesh Bansal
2022-11-16 08:02:03 +00:00
committed by Android (Google) Code Review
5 changed files with 508 additions and 11 deletions

View File

@@ -2694,7 +2694,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
int displayId, SensorManager sensorManager) {
return new DisplayPowerProximityStateController(wakelockController, displayDeviceConfig,
looper, nudgeUpdatePowerState,
displayId, sensorManager);
displayId, sensorManager, /* injector= */ null);
}
}

View File

@@ -30,6 +30,7 @@ import android.util.TimeUtils;
import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.utils.SensorUtils;
import java.io.PrintWriter;
@@ -40,16 +41,22 @@ import java.io.PrintWriter;
* state changes.
*/
public final class DisplayPowerProximityStateController {
private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 1;
@VisibleForTesting
static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 1;
@VisibleForTesting
static final int PROXIMITY_UNKNOWN = -1;
@VisibleForTesting
static final int PROXIMITY_POSITIVE = 1;
@VisibleForTesting
static final int PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY = 0;
private static final int MSG_IGNORE_PROXIMITY = 2;
private static final int PROXIMITY_UNKNOWN = -1;
private static final int PROXIMITY_NEGATIVE = 0;
private static final int PROXIMITY_POSITIVE = 1;
private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false;
// Proximity sensor debounce delay in milliseconds for positive transitions.
private static final int PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY = 0;
// Proximity sensor debounce delay in milliseconds for negative transitions.
private static final int PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY = 250;
// Trigger proximity if distance is less than 5 cm.
@@ -66,12 +73,13 @@ public final class DisplayPowerProximityStateController {
private final DisplayPowerProximityStateHandler mHandler;
// A runnable to execute the utility to update the power state.
private final Runnable mNudgeUpdatePowerState;
private Clock mClock;
// A listener which listen's to the events emitted by the proximity sensor.
private final SensorEventListener mProximitySensorListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
if (mProximitySensorEnabled) {
final long time = SystemClock.uptimeMillis();
final long time = mClock.uptimeMillis();
final float distance = event.values[0];
boolean positive = distance >= 0.0f && distance < mProximityThreshold;
handleProximitySensorEvent(time, positive);
@@ -147,7 +155,12 @@ public final class DisplayPowerProximityStateController {
public DisplayPowerProximityStateController(
WakelockController wakeLockController, DisplayDeviceConfig displayDeviceConfig,
Looper looper,
Runnable nudgeUpdatePowerState, int displayId, SensorManager sensorManager) {
Runnable nudgeUpdatePowerState, int displayId, SensorManager sensorManager,
Injector injector) {
if (injector == null) {
injector = new Injector();
}
mClock = injector.createClock();
mWakelockController = wakeLockController;
mHandler = new DisplayPowerProximityStateHandler(looper);
mNudgeUpdatePowerState = nudgeUpdatePowerState;
@@ -239,7 +252,6 @@ public final class DisplayPowerProximityStateController {
setProximitySensorEnabled(false);
mWaitingForNegativeProximity = false;
}
if (mScreenOffBecauseOfProximity
&& (mProximity != PROXIMITY_POSITIVE || mIgnoreProximityUntilChanged)) {
// The screen *was* off due to prox being near, but now it's "far" so lets turn
@@ -313,7 +325,7 @@ public final class DisplayPowerProximityStateController {
+ mSkipRampBecauseOfProximityChangeToNegative);
}
private void ignoreProximitySensorUntilChangedInternal() {
void ignoreProximitySensorUntilChangedInternal() {
if (!mIgnoreProximityUntilChanged
&& mProximity == PROXIMITY_POSITIVE) {
// Only ignore if it is still reporting positive (near)
@@ -414,7 +426,7 @@ public final class DisplayPowerProximityStateController {
if (mProximitySensorEnabled
&& mPendingProximity != PROXIMITY_UNKNOWN
&& mPendingProximityDebounceTime >= 0) {
final long now = SystemClock.uptimeMillis();
final long now = mClock.uptimeMillis();
if (mPendingProximityDebounceTime <= now) {
if (mProximity != mPendingProximity) {
// if the status of the sensor changed, stop ignoring.
@@ -473,4 +485,66 @@ public final class DisplayPowerProximityStateController {
}
}
@VisibleForTesting
boolean getPendingWaitForNegativeProximityLocked() {
synchronized (mLock) {
return mPendingWaitForNegativeProximityLocked;
}
}
@VisibleForTesting
boolean getWaitingForNegativeProximity() {
return mWaitingForNegativeProximity;
}
@VisibleForTesting
boolean shouldIgnoreProximityUntilChanged() {
return mIgnoreProximityUntilChanged;
}
boolean isProximitySensorEnabled() {
return mProximitySensorEnabled;
}
@VisibleForTesting
Handler getHandler() {
return mHandler;
}
@VisibleForTesting
int getPendingProximity() {
return mPendingProximity;
}
@VisibleForTesting
int getProximity() {
return mProximity;
}
@VisibleForTesting
long getPendingProximityDebounceTime() {
return mPendingProximityDebounceTime;
}
@VisibleForTesting
SensorEventListener getProximitySensorListener() {
return mProximitySensorListener;
}
/** Functional interface for providing time. */
@VisibleForTesting
interface Clock {
/**
* Returns current time in milliseconds since boot, not counting time spent in deep sleep.
*/
long uptimeMillis();
}
@VisibleForTesting
static class Injector {
Clock createClock() {
return () -> SystemClock.uptimeMillis();
}
}
}

View File

@@ -144,7 +144,7 @@ public final class DisplayPowerController2Test {
SensorManager sensorManager) {
return new DisplayPowerProximityStateController(wakelockController,
displayDeviceConfig, looper, nudgeUpdatePowerState, displayId,
sensorManager);
sensorManager, /* injector= */ null);
}
};

View File

@@ -0,0 +1,407 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.display;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.DisplayManagerInternal;
import android.os.test.TestLooper;
import android.view.Display;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.testutils.OffsettableClock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
public final class DisplayPowerProximityStateControllerTest {
@Mock
WakelockController mWakelockController;
@Mock
DisplayDeviceConfig mDisplayDeviceConfig;
@Mock
Runnable mNudgeUpdatePowerState;
@Mock
SensorManager mSensorManager;
private Sensor mProximitySensor;
private OffsettableClock mClock;
private TestLooper mTestLooper;
private SensorEventListener mSensorEventListener;
private DisplayPowerProximityStateController mDisplayPowerProximityStateController;
@Before
public void before() throws Exception {
MockitoAnnotations.initMocks(this);
mClock = new OffsettableClock.Stopped();
mTestLooper = new TestLooper(mClock::now);
when(mDisplayDeviceConfig.getProximitySensor()).thenReturn(
new DisplayDeviceConfig.SensorData() {
{
type = Sensor.STRING_TYPE_PROXIMITY;
// This is kept null because currently there is no way to define a sensor
// name in TestUtils
name = null;
}
});
setUpProxSensor();
DisplayPowerProximityStateController.Injector injector =
new DisplayPowerProximityStateController.Injector() {
@Override
DisplayPowerProximityStateController.Clock createClock() {
return new DisplayPowerProximityStateController.Clock() {
@Override
public long uptimeMillis() {
return mClock.now();
}
};
}
};
mDisplayPowerProximityStateController = new DisplayPowerProximityStateController(
mWakelockController, mDisplayDeviceConfig, mTestLooper.getLooper(),
mNudgeUpdatePowerState, 0,
mSensorManager, injector);
mSensorEventListener = mDisplayPowerProximityStateController.getProximitySensorListener();
}
@Test
public void updatePendingProximityRequestsWorksAsExpectedWhenPending() {
// Set the system to pending wait for proximity
assertTrue(mDisplayPowerProximityStateController.setPendingWaitForNegativeProximityLocked(
true));
assertTrue(
mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
// Update the pending proximity wait request
mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked();
assertTrue(mDisplayPowerProximityStateController.getWaitingForNegativeProximity());
assertFalse(
mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
}
@Test
public void updatePendingProximityRequestsWorksAsExpectedWhenNotPending() {
// Will not wait or be in the pending wait state of not already pending
mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked();
assertFalse(mDisplayPowerProximityStateController.getWaitingForNegativeProximity());
assertFalse(
mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
}
@Test
public void updatePendingProximityRequestsWorksAsExpectedWhenPendingAndProximityIgnored()
throws Exception {
// Set the system to the state where it will ignore proximity unless changed
enableProximitySensor();
emitAndValidatePositiveProximityEvent();
mDisplayPowerProximityStateController.ignoreProximitySensorUntilChangedInternal();
advanceTime(1);
assertTrue(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
verify(mNudgeUpdatePowerState, times(2)).run();
// Do not set the system to pending wait for proximity
mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked();
assertFalse(mDisplayPowerProximityStateController.getWaitingForNegativeProximity());
assertFalse(
mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
// Set the system to pending wait for proximity. But because the proximity is being
// ignored, it will not wait or not set the pending wait
assertTrue(mDisplayPowerProximityStateController.setPendingWaitForNegativeProximityLocked(
true));
mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked();
assertFalse(mDisplayPowerProximityStateController.getWaitingForNegativeProximity());
assertFalse(
mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
}
@Test
public void cleanupDisablesTheProximitySensor() {
enableProximitySensor();
mDisplayPowerProximityStateController.cleanup();
verify(mSensorManager).unregisterListener(
mSensorEventListener);
assertFalse(mDisplayPowerProximityStateController.isProximitySensorEnabled());
assertFalse(mDisplayPowerProximityStateController.getWaitingForNegativeProximity());
assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
assertEquals(mDisplayPowerProximityStateController.getProximity(),
DisplayPowerProximityStateController.PROXIMITY_UNKNOWN);
when(mWakelockController.releaseWakelock(
WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE)).thenReturn(true);
assertEquals(mDisplayPowerProximityStateController.getPendingProximityDebounceTime(), -1);
}
@Test
public void isProximitySensorAvailableReturnsTrueWhenAvailable() {
assertTrue(mDisplayPowerProximityStateController.isProximitySensorAvailable());
}
@Test
public void isProximitySensorAvailableReturnsFalseWhenNotAvailable() {
when(mDisplayDeviceConfig.getProximitySensor()).thenReturn(
new DisplayDeviceConfig.SensorData() {
{
type = null;
name = null;
}
});
mDisplayPowerProximityStateController = new DisplayPowerProximityStateController(
mWakelockController, mDisplayDeviceConfig, mTestLooper.getLooper(),
mNudgeUpdatePowerState, 1,
mSensorManager, null);
assertFalse(mDisplayPowerProximityStateController.isProximitySensorAvailable());
}
@Test
public void notifyDisplayDeviceChangedReloadsTheProximitySensor() throws Exception {
DisplayDeviceConfig updatedDisplayDeviceConfig = mock(DisplayDeviceConfig.class);
when(updatedDisplayDeviceConfig.getProximitySensor()).thenReturn(
new DisplayDeviceConfig.SensorData() {
{
type = Sensor.STRING_TYPE_PROXIMITY;
name = null;
}
});
Sensor newProxSensor = TestUtils.createSensor(
Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY, 4.0f);
when(mSensorManager.getSensorList(eq(Sensor.TYPE_ALL)))
.thenReturn(List.of(newProxSensor));
mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(
updatedDisplayDeviceConfig);
assertTrue(mDisplayPowerProximityStateController.isProximitySensorAvailable());
}
@Test
public void setPendingWaitForNegativeProximityLockedWorksAsExpected() {
// Doesn't do anything not asked to wait
assertFalse(mDisplayPowerProximityStateController.setPendingWaitForNegativeProximityLocked(
false));
assertFalse(
mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
// Sets pending wait negative proximity if not already waiting
assertTrue(mDisplayPowerProximityStateController.setPendingWaitForNegativeProximityLocked(
true));
assertTrue(
mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
// Will not set pending wait negative proximity if already waiting
assertFalse(mDisplayPowerProximityStateController.setPendingWaitForNegativeProximityLocked(
true));
assertTrue(
mDisplayPowerProximityStateController.getPendingWaitForNegativeProximityLocked());
}
@Test
public void evaluateProximityStateWhenRequestedUseOfProximitySensor() throws Exception {
// Enable the proximity sensor
enableProximitySensor();
// Emit a positive proximity event to move the system to a state to mimic a scenario
// where the system is in positive proximity
emitAndValidatePositiveProximityEvent();
// Again evaluate the proximity state, with system having positive proximity
setScreenOffBecauseOfPositiveProximityState();
}
@Test
public void evaluateProximityStateWhenScreenOffBecauseOfPositiveProximity() throws Exception {
// Enable the proximity sensor
enableProximitySensor();
// Emit a positive proximity event to move the system to a state to mimic a scenario
// where the system is in positive proximity
emitAndValidatePositiveProximityEvent();
// Again evaluate the proximity state, with system having positive proximity
setScreenOffBecauseOfPositiveProximityState();
// Set the system to pending wait for proximity
mDisplayPowerProximityStateController.setPendingWaitForNegativeProximityLocked(true);
// Update the pending proximity wait request
mDisplayPowerProximityStateController.updatePendingProximityRequestsLocked();
// Start ignoring proximity sensor
mDisplayPowerProximityStateController.ignoreProximitySensorUntilChangedInternal();
// Re-evaluate the proximity state, such that the system is detecting the positive
// proximity, and screen is off because of that
when(mWakelockController.getOnProximityNegativeRunnable()).thenReturn(mock(Runnable.class));
mDisplayPowerProximityStateController.updateProximityState(mock(
DisplayManagerInternal.DisplayPowerRequest.class), Display.STATE_ON);
assertTrue(mDisplayPowerProximityStateController.isProximitySensorEnabled());
assertFalse(mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity());
assertTrue(
mDisplayPowerProximityStateController
.shouldSkipRampBecauseOfProximityChangeToNegative());
verify(mWakelockController).acquireWakelock(
WakelockController.WAKE_LOCK_PROXIMITY_NEGATIVE);
}
@Test
public void evaluateProximityStateWhenDisplayIsTurningOff() throws Exception {
// Enable the proximity sensor
enableProximitySensor();
// Emit a positive proximity event to move the system to a state to mimic a scenario
// where the system is in positive proximity
emitAndValidatePositiveProximityEvent();
// Again evaluate the proximity state, with system having positive proximity
setScreenOffBecauseOfPositiveProximityState();
// Re-evaluate the proximity state, such that the system is detecting the positive
// proximity, and screen is off because of that
mDisplayPowerProximityStateController.updateProximityState(mock(
DisplayManagerInternal.DisplayPowerRequest.class), Display.STATE_OFF);
verify(mSensorManager).unregisterListener(
mSensorEventListener);
assertFalse(mDisplayPowerProximityStateController.isProximitySensorEnabled());
assertFalse(mDisplayPowerProximityStateController.getWaitingForNegativeProximity());
assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
assertEquals(mDisplayPowerProximityStateController.getProximity(),
DisplayPowerProximityStateController.PROXIMITY_UNKNOWN);
when(mWakelockController.releaseWakelock(
WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE)).thenReturn(true);
assertEquals(mDisplayPowerProximityStateController.getPendingProximityDebounceTime(), -1);
}
@Test
public void evaluateProximityStateNotWaitingForNegativeProximityAndNotUsingProxSensor()
throws Exception {
// Enable the proximity sensor
enableProximitySensor();
// Emit a positive proximity event to move the system to a state to mimic a scenario
// where the system is in positive proximity
emitAndValidatePositiveProximityEvent();
// Re-evaluate the proximity state, such that the system is detecting the positive
// proximity, and screen is off because of that
mDisplayPowerProximityStateController.updateProximityState(mock(
DisplayManagerInternal.DisplayPowerRequest.class), Display.STATE_ON);
verify(mSensorManager).unregisterListener(
mSensorEventListener);
assertFalse(mDisplayPowerProximityStateController.isProximitySensorEnabled());
assertFalse(mDisplayPowerProximityStateController.getWaitingForNegativeProximity());
assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
assertEquals(mDisplayPowerProximityStateController.getProximity(),
DisplayPowerProximityStateController.PROXIMITY_UNKNOWN);
when(mWakelockController.releaseWakelock(
WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE)).thenReturn(true);
assertEquals(mDisplayPowerProximityStateController.getPendingProximityDebounceTime(), -1);
}
private void advanceTime(long timeMs) {
mClock.fastForward(timeMs);
mTestLooper.dispatchAll();
}
private void setUpProxSensor() throws Exception {
mProximitySensor = TestUtils.createSensor(
Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY, 5.0f);
when(mSensorManager.getSensorList(eq(Sensor.TYPE_ALL)))
.thenReturn(List.of(mProximitySensor));
}
private void emitAndValidatePositiveProximityEvent() throws Exception {
// Emit a positive proximity event to move the system to a state to mimic a scenario
// where the system is in positive proximity
when(mWakelockController.releaseWakelock(
WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE)).thenReturn(true);
mSensorEventListener.onSensorChanged(TestUtils.createSensorEvent(mProximitySensor, 4));
verify(mSensorManager).registerListener(mSensorEventListener,
mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL,
mDisplayPowerProximityStateController.getHandler());
verify(mWakelockController).acquireWakelock(
WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE);
assertEquals(mDisplayPowerProximityStateController.getPendingProximity(),
DisplayPowerProximityStateController.PROXIMITY_POSITIVE);
assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
assertEquals(mDisplayPowerProximityStateController.getProximity(),
DisplayPowerProximityStateController.PROXIMITY_POSITIVE);
verify(mNudgeUpdatePowerState).run();
assertEquals(mDisplayPowerProximityStateController.getPendingProximityDebounceTime(), -1);
}
// Call evaluateProximityState with the request for using the proximity sensor. This will
// register the proximity sensor listener, which will be needed for mocking positive
// proximity scenarios.
private void enableProximitySensor() {
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
DisplayManagerInternal.DisplayPowerRequest.class);
displayPowerRequest.useProximitySensor = true;
mDisplayPowerProximityStateController.updateProximityState(displayPowerRequest,
Display.STATE_ON);
verify(mSensorManager).registerListener(
mSensorEventListener,
mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL,
mDisplayPowerProximityStateController.getHandler());
assertTrue(mDisplayPowerProximityStateController.isProximitySensorEnabled());
assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
assertFalse(mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity());
verifyZeroInteractions(mWakelockController);
}
private void setScreenOffBecauseOfPositiveProximityState() {
// Prepare a request to indicate that the proximity sensor is to be used
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
DisplayManagerInternal.DisplayPowerRequest.class);
displayPowerRequest.useProximitySensor = true;
Runnable onProximityPositiveRunnable = mock(Runnable.class);
when(mWakelockController.getOnProximityPositiveRunnable()).thenReturn(
onProximityPositiveRunnable);
mDisplayPowerProximityStateController.updateProximityState(displayPowerRequest,
Display.STATE_ON);
verify(mSensorManager).registerListener(
mSensorEventListener,
mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL,
mDisplayPowerProximityStateController.getHandler());
assertTrue(mDisplayPowerProximityStateController.isProximitySensorEnabled());
assertFalse(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
assertTrue(mDisplayPowerProximityStateController.isScreenOffBecauseOfProximity());
verify(mWakelockController).acquireWakelock(
WakelockController.WAKE_LOCK_PROXIMITY_POSITIVE);
}
}

View File

@@ -51,6 +51,12 @@ public final class TestUtils {
}
}
public static void setMaximumRange(Sensor sensor, float maximumRange) throws Exception {
Method setter = Sensor.class.getDeclaredMethod("setRange", Float.TYPE, Float.TYPE);
setter.setAccessible(true);
setter.invoke(sensor, maximumRange, 1);
}
public static Sensor createSensor(int type, String strType) throws Exception {
Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
constr.setAccessible(true);
@@ -59,6 +65,16 @@ public final class TestUtils {
return sensor;
}
public static Sensor createSensor(int type, String strType, float maximumRange)
throws Exception {
Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
constr.setAccessible(true);
Sensor sensor = constr.newInstance();
setSensorType(sensor, type, strType);
setMaximumRange(sensor, maximumRange);
return sensor;
}
/**
* Create a custom {@link DisplayAddress} to ensure we're not relying on any specific
* display-address implementation in our code. Intentionally uses default object (reference)