Merge changes I9ccdbd8c,Ifc24ba37

* changes:
  Add onDeviceStateChanged() callback to DeviceStateManager.
  Fix DeviceStateManagerServiceTests and add to presubmit.
This commit is contained in:
Darryl Johnson
2020-11-06 20:38:27 +00:00
committed by Android (Google) Code Review
11 changed files with 602 additions and 8 deletions

View File

@@ -16,9 +16,12 @@
package android.hardware.devicestate;
import android.annotation.NonNull;
import android.annotation.SystemService;
import android.content.Context;
import java.util.concurrent.Executor;
/**
* Manages the state of the system for devices with user-configurable hardware like a foldable
* phone.
@@ -33,6 +36,47 @@ public final class DeviceStateManager {
private DeviceStateManagerGlobal mGlobal;
public DeviceStateManager() {
mGlobal = DeviceStateManagerGlobal.getInstance();
DeviceStateManagerGlobal global = DeviceStateManagerGlobal.getInstance();
if (global == null) {
throw new IllegalStateException(
"Failed to get instance of global device state manager.");
}
mGlobal = global;
}
/**
* Registers a listener to receive notifications about changes in device state.
*
* @param listener the listener to register.
* @param executor the executor to process notifications.
*
* @see DeviceStateListener
*/
public void registerDeviceStateListener(@NonNull DeviceStateListener listener,
@NonNull Executor executor) {
mGlobal.registerDeviceStateListener(listener, executor);
}
/**
* Unregisters a listener previously registered with
* {@link #registerDeviceStateListener(DeviceStateListener, Executor)}.
*/
public void unregisterDeviceStateListener(@NonNull DeviceStateListener listener) {
mGlobal.unregisterDeviceStateListener(listener);
}
/**
* Listens for changes in device states.
*/
public interface DeviceStateListener {
/**
* Called in response to device state changes.
* <p>
* Guaranteed to be called once on registration of the listener with the
* initial value and then on every subsequent change in device state.
*
* @param deviceState the new device state.
*/
void onDeviceStateChanged(int deviceState);
}
}

View File

@@ -19,16 +19,26 @@ package android.hardware.devicestate;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager.DeviceStateListener;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import java.util.ArrayList;
import java.util.concurrent.Executor;
/**
* Provides communication with the device state system service on behalf of applications.
*
* @see DeviceStateManager
* @hide
*/
final class DeviceStateManagerGlobal {
@VisibleForTesting(visibility = Visibility.PACKAGE)
public final class DeviceStateManagerGlobal {
private static DeviceStateManagerGlobal sInstance;
/**
@@ -49,10 +59,121 @@ final class DeviceStateManagerGlobal {
}
}
private final Object mLock = new Object();
@NonNull
private final IDeviceStateManager mDeviceStateManager;
@Nullable
private DeviceStateManagerCallback mCallback;
private DeviceStateManagerGlobal(@NonNull IDeviceStateManager deviceStateManager) {
@GuardedBy("mLock")
private final ArrayList<DeviceStateListenerWrapper> mListeners = new ArrayList<>();
@Nullable
@GuardedBy("mLock")
private Integer mLastReceivedState;
@VisibleForTesting
public DeviceStateManagerGlobal(@NonNull IDeviceStateManager deviceStateManager) {
mDeviceStateManager = deviceStateManager;
}
/**
* Registers a listener to receive notifications about changes in device state.
*
* @see DeviceStateManager#registerDeviceStateListener(DeviceStateListener, Executor)
*/
@VisibleForTesting(visibility = Visibility.PACKAGE)
public void registerDeviceStateListener(@NonNull DeviceStateListener listener,
@NonNull Executor executor) {
Integer stateToReport;
DeviceStateListenerWrapper wrapper;
synchronized (mLock) {
registerCallbackIfNeededLocked();
int index = findListenerLocked(listener);
if (index != -1) {
// This listener is already registered.
return;
}
wrapper = new DeviceStateListenerWrapper(listener, executor);
mListeners.add(wrapper);
stateToReport = mLastReceivedState;
}
if (stateToReport != null) {
// Notify the listener with the most recent device state from the server. If the state
// to report is null this is likely the first listener added and we're still waiting
// from the callback from the server.
wrapper.notifyDeviceStateChanged(stateToReport);
}
}
/**
* Unregisters a listener previously registered with
* {@link #registerDeviceStateListener(DeviceStateListener, Executor)}.
*
* @see DeviceStateManager#registerDeviceStateListener(DeviceStateListener, Executor)
*/
@VisibleForTesting(visibility = Visibility.PACKAGE)
public void unregisterDeviceStateListener(DeviceStateListener listener) {
synchronized (mLock) {
int indexToRemove = findListenerLocked(listener);
if (indexToRemove != -1) {
mListeners.remove(indexToRemove);
}
}
}
private void registerCallbackIfNeededLocked() {
if (mCallback == null) {
mCallback = new DeviceStateManagerCallback();
try {
mDeviceStateManager.registerCallback(mCallback);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
private int findListenerLocked(DeviceStateListener listener) {
for (int i = 0; i < mListeners.size(); i++) {
if (mListeners.get(i).mDeviceStateListener.equals(listener)) {
return i;
}
}
return -1;
}
private void handleDeviceStateChanged(int newDeviceState) {
ArrayList<DeviceStateListenerWrapper> listeners;
synchronized (mLock) {
mLastReceivedState = newDeviceState;
listeners = new ArrayList<>(mListeners);
}
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).notifyDeviceStateChanged(newDeviceState);
}
}
private final class DeviceStateManagerCallback extends IDeviceStateManagerCallback.Stub {
@Override
public void onDeviceStateChanged(int deviceState) {
handleDeviceStateChanged(deviceState);
}
}
private static final class DeviceStateListenerWrapper {
private final DeviceStateListener mDeviceStateListener;
private final Executor mExecutor;
DeviceStateListenerWrapper(DeviceStateListener listener, Executor executor) {
mDeviceStateListener = listener;
mExecutor = executor;
}
void notifyDeviceStateChanged(int newDeviceState) {
mExecutor.execute(() -> mDeviceStateListener.onDeviceStateChanged(newDeviceState));
}
}
}

View File

@@ -16,5 +16,9 @@
package android.hardware.devicestate;
import android.hardware.devicestate.IDeviceStateManagerCallback;
/** @hide */
interface IDeviceStateManager {}
interface IDeviceStateManager {
void registerCallback(in IDeviceStateManagerCallback callback);
}

View File

@@ -0,0 +1,22 @@
/**
* 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 android.hardware.devicestate;
/** @hide */
interface IDeviceStateManagerCallback {
oneway void onDeviceStateChanged(int deviceState);
}

View File

@@ -0,0 +1,26 @@
// 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.
android_test {
name: "FrameworksCoreDeviceStateManagerTests",
// Include all test java files
srcs: ["src/**/*.java"],
static_libs: [
"androidx.test.rules",
"frameworks-base-testutils",
],
libs: ["android.test.runner"],
platform_apis: true,
certificate: "platform",
}

View File

@@ -0,0 +1,29 @@
<?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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.frameworks.coretests.devicestate"
android:sharedUserId="android.uid.system" >
<application>
<uses-library android:name="android.test.runner" />
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.frameworks.coretests.devicestate"
android:label="Device State Manager Tests"/>
</manifest>

View File

@@ -0,0 +1,31 @@
<?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.
-->
<configuration description="Runs Device State Manager Tests.">
<option name="test-suite-tag" value="apct"/>
<option name="test-suite-tag" value="apct-instrumentation"/>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="FrameworksCoreDeviceStateManagerTests.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.frameworks.coretests.devicestate" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false" />
</test>
</configuration>

View File

@@ -0,0 +1,135 @@
/*
* 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 android.hardware.devicestate;
import static junit.framework.Assert.assertEquals;
import android.annotation.Nullable;
import android.os.RemoteException;
import androidx.test.filters.SmallTest;
import com.android.internal.util.ConcurrentUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.HashSet;
import java.util.Set;
/**
* Unit tests for {@link DeviceStateManagerGlobal}.
* <p/>
* Run with <code>atest DeviceStateManagerGlobalTest</code>.
*/
@RunWith(JUnit4.class)
@SmallTest
public final class DeviceStateManagerGlobalTest {
private TestDeviceStateManagerService mService;
private DeviceStateManagerGlobal mDeviceStateManagerGlobal;
@Before
public void setUp() {
mService = new TestDeviceStateManagerService();
mDeviceStateManagerGlobal = new DeviceStateManagerGlobal(mService);
}
@Test
public void registerListener() {
mService.setDeviceState(0);
TestDeviceStateListener listener1 = new TestDeviceStateListener();
TestDeviceStateListener listener2 = new TestDeviceStateListener();
mDeviceStateManagerGlobal.registerDeviceStateListener(listener1,
ConcurrentUtils.DIRECT_EXECUTOR);
mDeviceStateManagerGlobal.registerDeviceStateListener(listener2,
ConcurrentUtils.DIRECT_EXECUTOR);
assertEquals(0, listener1.getLastReportedState().intValue());
assertEquals(0, listener2.getLastReportedState().intValue());
mService.setDeviceState(1);
assertEquals(1, listener1.getLastReportedState().intValue());
assertEquals(1, listener2.getLastReportedState().intValue());
}
@Test
public void unregisterListener() {
mService.setDeviceState(0);
TestDeviceStateListener listener = new TestDeviceStateListener();
mDeviceStateManagerGlobal.registerDeviceStateListener(listener,
ConcurrentUtils.DIRECT_EXECUTOR);
assertEquals(0, listener.getLastReportedState().intValue());
mDeviceStateManagerGlobal.unregisterDeviceStateListener(listener);
mService.setDeviceState(1);
assertEquals(0, listener.getLastReportedState().intValue());
}
private final class TestDeviceStateListener implements DeviceStateManager.DeviceStateListener {
@Nullable
private Integer mLastReportedDeviceState;
@Override
public void onDeviceStateChanged(int deviceState) {
mLastReportedDeviceState = deviceState;
}
@Nullable
public Integer getLastReportedState() {
return mLastReportedDeviceState;
}
}
private final class TestDeviceStateManagerService extends IDeviceStateManager.Stub {
private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
private Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>();
@Override
public void registerCallback(IDeviceStateManagerCallback callback) {
if (mCallbacks.contains(callback)) {
throw new SecurityException("Callback is already registered.");
}
mCallbacks.add(callback);
try {
callback.onDeviceStateChanged(mDeviceState);
} catch (RemoteException e) {
// Do nothing. Should never happen.
}
}
public void setDeviceState(int deviceState) {
boolean stateChanged = mDeviceState != deviceState;
mDeviceState = deviceState;
if (stateChanged) {
for (IDeviceStateManagerCallback callback : mCallbacks) {
try {
callback.onDeviceStateChanged(mDeviceState);
} catch (RemoteException e) {
// Do nothing. Should never happen.
}
}
}
}
}
}

View File

@@ -16,18 +16,22 @@
package com.android.server.devicestate;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
import static android.Manifest.permission.CONTROL_DEVICE_STATE;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.devicestate.IDeviceStateManager;
import android.hardware.devicestate.IDeviceStateManagerCallback;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -37,6 +41,7 @@ import com.android.server.policy.DeviceStatePolicyImpl;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
/**
@@ -66,6 +71,8 @@ public final class DeviceStateManagerService extends SystemService {
private final Object mLock = new Object();
@NonNull
private final DeviceStatePolicy mDeviceStatePolicy;
@NonNull
private final BinderService mBinderService;
@GuardedBy("mLock")
private IntArray mSupportedDeviceStates;
@@ -88,6 +95,10 @@ public final class DeviceStateManagerService extends SystemService {
@GuardedBy("mLock")
private int mRequestedOverrideState = INVALID_DEVICE_STATE;
// List of registered callbacks indexed by process id.
@GuardedBy("mLock")
private final SparseArray<CallbackRecord> mCallbacks = new SparseArray<>();
public DeviceStateManagerService(@NonNull Context context) {
this(context, new DeviceStatePolicyImpl());
}
@@ -96,12 +107,13 @@ public final class DeviceStateManagerService extends SystemService {
DeviceStateManagerService(@NonNull Context context, @NonNull DeviceStatePolicy policy) {
super(context);
mDeviceStatePolicy = policy;
mDeviceStatePolicy.getDeviceStateProvider().setListener(new DeviceStateProviderListener());
mBinderService = new BinderService();
}
@Override
public void onStart() {
mDeviceStatePolicy.getDeviceStateProvider().setListener(new DeviceStateProviderListener());
publishBinderService(Context.DEVICE_STATE_SERVICE, new BinderService());
publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService);
}
/**
@@ -186,6 +198,11 @@ public final class DeviceStateManagerService extends SystemService {
}
}
@VisibleForTesting
IDeviceStateManager getBinderService() {
return mBinderService;
}
private void updateSupportedStates(int[] supportedDeviceStates) {
// Must ensure sorted as isSupportedStateLocked() impl uses binary search.
Arrays.sort(supportedDeviceStates, 0, supportedDeviceStates.length);
@@ -310,18 +327,81 @@ public final class DeviceStateManagerService extends SystemService {
* </p>
*/
private void commitPendingState() {
// Update the current state.
int newState;
synchronized (mLock) {
if (DEBUG) {
Slog.d(TAG, "Committing state: " + mPendingState);
}
mCommittedState = mPendingState;
newState = mCommittedState;
mPendingState = INVALID_DEVICE_STATE;
updatePendingStateLocked();
}
// Notify callbacks of a change.
notifyDeviceStateChanged(newState);
// Try to configure the next state if needed.
notifyPolicyIfNeeded();
}
private void notifyDeviceStateChanged(int deviceState) {
if (Thread.holdsLock(mLock)) {
throw new IllegalStateException(
"Attempting to notify callbacks with service lock held.");
}
// Grab the lock and copy the callbacks.
ArrayList<CallbackRecord> callbacks;
synchronized (mLock) {
if (mCallbacks.size() == 0) {
return;
}
callbacks = new ArrayList<>();
for (int i = 0; i < mCallbacks.size(); i++) {
callbacks.add(mCallbacks.valueAt(i));
}
}
// After releasing the lock, send the notifications out.
for (int i = 0; i < callbacks.size(); i++) {
callbacks.get(i).notifyDeviceStateAsync(deviceState);
}
}
private void registerCallbackInternal(IDeviceStateManagerCallback callback, int callingPid) {
int currentState;
CallbackRecord record;
// Grab the lock to register the callback and get the current state.
synchronized (mLock) {
if (mCallbacks.contains(callingPid)) {
throw new SecurityException("The calling process has already registered an"
+ " IDeviceStateManagerCallback.");
}
record = new CallbackRecord(callback, callingPid);
try {
callback.asBinder().linkToDeath(record, 0);
} catch (RemoteException ex) {
throw new RuntimeException(ex);
}
mCallbacks.put(callingPid, record);
currentState = mCommittedState;
}
// Notify the callback of the state at registration.
record.notifyDeviceStateAsync(currentState);
}
private void unregisterCallbackInternal(CallbackRecord record) {
synchronized (mLock) {
mCallbacks.remove(record.mPid);
}
}
private void dumpInternal(PrintWriter pw) {
pw.println("DEVICE STATE MANAGER (dumpsys device_state)");
@@ -330,6 +410,14 @@ public final class DeviceStateManagerService extends SystemService {
pw.println(" mPendingState=" + toString(mPendingState));
pw.println(" mRequestedState=" + toString(mRequestedState));
pw.println(" mRequestedOverrideState=" + toString(mRequestedOverrideState));
final int callbackCount = mCallbacks.size();
pw.println();
pw.println("Callbacks: size=" + callbackCount);
for (int i = 0; i < callbackCount; i++) {
CallbackRecord callback = mCallbacks.valueAt(i);
pw.println(" " + i + ": mPid=" + callback.mPid);
}
}
}
@@ -360,8 +448,47 @@ public final class DeviceStateManagerService extends SystemService {
}
}
private final class CallbackRecord implements IBinder.DeathRecipient {
private final IDeviceStateManagerCallback mCallback;
private final int mPid;
CallbackRecord(IDeviceStateManagerCallback callback, int pid) {
mCallback = callback;
mPid = pid;
}
@Override
public void binderDied() {
unregisterCallbackInternal(this);
}
public void notifyDeviceStateAsync(int devicestate) {
try {
mCallback.onDeviceStateChanged(devicestate);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to notify process " + mPid + " that device state changed.",
ex);
}
}
}
/** Implementation of {@link IDeviceStateManager} published as a binder service. */
private final class BinderService extends IDeviceStateManager.Stub {
@Override // Binder call
public void registerCallback(IDeviceStateManagerCallback callback) {
if (callback == null) {
throw new IllegalArgumentException("Device state callback must not be null.");
}
final int callingPid = Binder.getCallingPid();
final long token = Binder.clearCallingIdentity();
try {
registerCallbackInternal(callback, callingPid);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver result) {

View File

@@ -85,6 +85,7 @@
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.ACCESS_VIBRATOR_STATE"/>
<uses-permission android:name="android.permission.VIBRATE_ALWAYS_ON"/>
<uses-permission android:name="android.permission.CONTROL_DEVICE_STATE"/>
<!-- Uses API introduced in O (26) -->
<uses-sdk android:minSdkVersion="1"

View File

@@ -19,8 +19,13 @@ package com.android.server.devicestate;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertThrows;
import android.hardware.devicestate.IDeviceStateManagerCallback;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -28,11 +33,14 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import javax.annotation.Nullable;
/**
* Unit tests for {@link DeviceStateManagerService}.
* <p/>
* Run with <code>atest DeviceStateManagerServiceTest</code>.
*/
@Presubmit
@RunWith(AndroidJUnit4.class)
public final class DeviceStateManagerServiceTest {
private static final int DEFAULT_DEVICE_STATE = 0;
@@ -48,7 +56,6 @@ public final class DeviceStateManagerServiceTest {
mProvider = new TestDeviceStateProvider();
mPolicy = new TestDeviceStatePolicy(mProvider);
mService = new DeviceStateManagerService(InstrumentationRegistry.getContext(), mPolicy);
mService.onStart();
}
@Test
@@ -187,6 +194,38 @@ public final class DeviceStateManagerServiceTest {
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(), DEFAULT_DEVICE_STATE);
}
@Test
public void registerCallback() throws RemoteException {
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
mService.getBinderService().registerCallback(callback);
mProvider.notifyRequestState(OTHER_DEVICE_STATE);
assertNotNull(callback.getLastNotifiedValue());
assertEquals(callback.getLastNotifiedValue().intValue(), OTHER_DEVICE_STATE);
mProvider.notifyRequestState(DEFAULT_DEVICE_STATE);
assertEquals(callback.getLastNotifiedValue().intValue(), DEFAULT_DEVICE_STATE);
mPolicy.blockConfigure();
mProvider.notifyRequestState(OTHER_DEVICE_STATE);
// The callback should not have been notified of the state change as the policy is still
// pending callback.
assertEquals(callback.getLastNotifiedValue().intValue(), DEFAULT_DEVICE_STATE);
mPolicy.resumeConfigure();
// Now that the policy is finished processing the callback should be notified of the state
// change.
assertEquals(callback.getLastNotifiedValue().intValue(), OTHER_DEVICE_STATE);
}
@Test
public void registerCallback_emitsInitialValue() throws RemoteException {
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
mService.getBinderService().registerCallback(callback);
assertNotNull(callback.getLastNotifiedValue());
assertEquals(callback.getLastNotifiedValue().intValue(), DEFAULT_DEVICE_STATE);
}
private static final class TestDeviceStatePolicy implements DeviceStatePolicy {
private final DeviceStateProvider mProvider;
private int mLastDeviceStateRequestedToConfigure = INVALID_DEVICE_STATE;
@@ -262,4 +301,19 @@ public final class DeviceStateManagerServiceTest {
mListener.onStateChanged(state);
}
}
private static final class TestDeviceStateManagerCallback extends
IDeviceStateManagerCallback.Stub {
Integer mLastNotifiedValue;
@Override
public void onDeviceStateChanged(int deviceState) {
mLastNotifiedValue = deviceState;
}
@Nullable
Integer getLastNotifiedValue() {
return mLastNotifiedValue;
}
}
}