Merge changes I9ccdbd8c,Ifc24ba37
* changes: Add onDeviceStateChanged() callback to DeviceStateManager. Fix DeviceStateManagerServiceTests and add to presubmit.
This commit is contained in:
committed by
Android (Google) Code Review
commit
e4cbd03782
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,5 +16,9 @@
|
||||
|
||||
package android.hardware.devicestate;
|
||||
|
||||
import android.hardware.devicestate.IDeviceStateManagerCallback;
|
||||
|
||||
/** @hide */
|
||||
interface IDeviceStateManager {}
|
||||
interface IDeviceStateManager {
|
||||
void registerCallback(in IDeviceStateManagerCallback callback);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
26
core/tests/devicestatetests/Android.bp
Normal file
26
core/tests/devicestatetests/Android.bp
Normal 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",
|
||||
}
|
||||
29
core/tests/devicestatetests/AndroidManifest.xml
Normal file
29
core/tests/devicestatetests/AndroidManifest.xml
Normal 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>
|
||||
31
core/tests/devicestatetests/AndroidTest.xml
Normal file
31
core/tests/devicestatetests/AndroidTest.xml
Normal 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>
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user