Support input device lights manager feature in frameworks.

Add lights manager support to input frameworks.

Bug: 161633625
Test: atest LightsManagerTest, atest InputDeviceLightsManagerTest
Change-Id: Ic6c701cad84f277325583c77195592d96d9296b8
This commit is contained in:
Chris Ye
2021-01-06 18:42:54 -08:00
parent 4dd2f7f838
commit cbb480c25e
17 changed files with 1201 additions and 198 deletions

View File

@@ -25,6 +25,8 @@ import android.hardware.input.TouchCalibration;
import android.os.CombinedVibrationEffect;
import android.hardware.input.IInputSensorEventListener;
import android.hardware.input.InputSensorInfo;
import android.hardware.lights.Light;
import android.hardware.lights.LightState;
import android.os.IBinder;
import android.os.IVibratorStateListener;
import android.os.VibrationEffect;
@@ -127,4 +129,14 @@ interface IInputManager {
void disableSensor(int deviceId, int sensorType);
boolean flushSensor(int deviceId, int sensorType);
List<Light> getLights(int deviceId);
LightState getLightState(int deviceId, int lightId);
void setLightStates(int deviceId, in int[] lightIds, in LightState[] states, in IBinder token);
void openLightSession(int deviceId, String opPkg, in IBinder token);
void closeLightSession(int deviceId, in IBinder token);
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright (C) 2021 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.input;
import android.annotation.NonNull;
import android.app.ActivityThread;
import android.hardware.lights.Light;
import android.hardware.lights.LightState;
import android.hardware.lights.LightsManager;
import android.hardware.lights.LightsRequest;
import android.util.CloseGuard;
import com.android.internal.util.Preconditions;
import java.lang.ref.Reference;
import java.util.List;
/**
* LightsManager manages an input device's lights {@link android.hardware.input.Light}.
*/
class InputDeviceLightsManager extends LightsManager {
private static final String TAG = "InputDeviceLightsManager";
private static final boolean DEBUG = false;
private final InputManager mInputManager;
// The input device ID.
private final int mDeviceId;
// Package name
private final String mPackageName;
InputDeviceLightsManager(InputManager inputManager, int deviceId) {
super(ActivityThread.currentActivityThread().getSystemContext());
mInputManager = inputManager;
mDeviceId = deviceId;
mPackageName = ActivityThread.currentPackageName();
}
/**
* Returns the lights available on the device.
*
* @return A list of available lights
*/
@Override
public @NonNull List<Light> getLights() {
return mInputManager.getLights(mDeviceId);
}
/**
* Returns the state of a specified light.
*
* @hide
*/
@Override
public @NonNull LightState getLightState(@NonNull Light light) {
Preconditions.checkNotNull(light);
return mInputManager.getLightState(mDeviceId, light);
}
/**
* Creates a new LightsSession that can be used to control the device lights.
*/
@Override
public @NonNull LightsSession openSession() {
final LightsSession session = new InputDeviceLightsSession();
mInputManager.openLightSession(mDeviceId, mPackageName, session.getToken());
return session;
}
/**
* Encapsulates a session that can be used to control device lights and represents the lifetime
* of the requests.
*/
public final class InputDeviceLightsSession extends LightsManager.LightsSession
implements AutoCloseable {
private final CloseGuard mCloseGuard = new CloseGuard();
private boolean mClosed = false;
/**
* Instantiated by {@link LightsManager#openSession()}.
*/
private InputDeviceLightsSession() {
mCloseGuard.open("close");
}
/**
* Sends a request to modify the states of multiple lights.
*
* @param request the settings for lights that should change
*/
@Override
public void requestLights(@NonNull LightsRequest request) {
Preconditions.checkNotNull(request);
Preconditions.checkArgument(!mClosed);
mInputManager.requestLights(mDeviceId, request, getToken());
}
/**
* Closes the session, reverting all changes made through it.
*/
@Override
public void close() {
if (!mClosed) {
mInputManager.closeLightSession(mDeviceId, getToken());
mClosed = true;
mCloseGuard.close();
}
Reference.reachabilityFence(this);
}
/** @hide */
@Override
protected void finalize() throws Throwable {
try {
mCloseGuard.warnIfOpen();
close();
} finally {
super.finalize();
}
}
}
}

View File

@@ -30,6 +30,10 @@ import android.compat.annotation.ChangeId;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.hardware.SensorManager;
import android.hardware.lights.Light;
import android.hardware.lights.LightState;
import android.hardware.lights.LightsManager;
import android.hardware.lights.LightsRequest;
import android.os.BlockUntrustedTouchesMode;
import android.os.Build;
import android.os.CombinedVibrationEffect;
@@ -1409,7 +1413,7 @@ public final class InputManager {
}
/**
* Gets a vibrator service associated with an input device, always create a new instance.
* Gets a vibrator service associated with an input device, always creates a new instance.
* @return The vibrator, never null.
* @hide
*/
@@ -1418,7 +1422,7 @@ public final class InputManager {
}
/**
* Gets a vibrator manager service associated with an input device, always create a new
* Gets a vibrator manager service associated with an input device, always creates a new
* instance.
* @return The vibrator manager, never null.
* @hide
@@ -1486,10 +1490,8 @@ public final class InputManager {
/**
* Register input device vibrator state listener
*
* @hide
*/
public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) {
boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) {
try {
return mIm.registerVibratorStateListener(deviceId, listener);
} catch (RemoteException ex) {
@@ -1499,10 +1501,8 @@ public final class InputManager {
/**
* Unregister input device vibrator state listener
*
* @hide
*/
public boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) {
boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) {
try {
return mIm.unregisterVibratorStateListener(deviceId, listener);
} catch (RemoteException ex) {
@@ -1511,7 +1511,7 @@ public final class InputManager {
}
/**
* Gets a sensor manager service associated with an input device, always create a new instance.
* Gets a sensor manager service associated with an input device, always creates a new instance.
* @return The sensor manager, never null.
* @hide
*/
@@ -1532,6 +1532,86 @@ public final class InputManager {
return new InputDeviceBattery(this, deviceId, hasBattery);
}
/**
* Gets a lights manager associated with an input device, always creates a new instance.
* @return The lights manager, never null.
* @hide
*/
@NonNull
public LightsManager getInputDeviceLightsManager(int deviceId) {
return new InputDeviceLightsManager(InputManager.this, deviceId);
}
/**
* Gets a list of light objects associated with an input device.
* @return The list of lights, never null.
*/
@NonNull List<Light> getLights(int deviceId) {
try {
return mIm.getLights(deviceId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Returns the state of an input device light.
* @return the light state
*/
@NonNull LightState getLightState(int deviceId, @NonNull Light light) {
try {
return mIm.getLightState(deviceId, light.getId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Request to modify the states of multiple lights.
*
* @param request the settings for lights that should change
*/
void requestLights(int deviceId, @NonNull LightsRequest request, IBinder token) {
try {
List<Integer> lightIdList = request.getLights();
int[] lightIds = new int[lightIdList.size()];
for (int i = 0; i < lightIds.length; i++) {
lightIds[i] = lightIdList.get(i);
}
List<LightState> lightStateList = request.getLightStates();
mIm.setLightStates(deviceId, lightIds,
lightStateList.toArray(new LightState[lightStateList.size()]),
token);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Open light session for input device manager
*
* @param token The token for the light session
*/
void openLightSession(int deviceId, String opPkg, @NonNull IBinder token) {
try {
mIm.openLightSession(deviceId, opPkg, token);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Close light session
*
*/
void closeLightSession(int deviceId, @NonNull IBinder token) {
try {
mIm.closeLightSession(deviceId, token);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Listens for changes in input devices.
*/

View File

@@ -16,22 +16,56 @@
package android.hardware.lights;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Represents a logical light on the device.
*
* @hide
*/
@SystemApi
public final class Light implements Parcelable {
// These enum values copy the values from {@link com.android.server.lights.LightsManager}
// and the light HAL. Since 0-7 are lights reserved for system use, 8 for microphone light is
// defined in {@link android.hardware.lights.LightsManager}, following types are available
// through this API.
/** Type for lights that indicate microphone usage */
public static final int LIGHT_TYPE_MICROPHONE = 8;
/**
* Type for lights that indicate a monochrome color LED light.
*/
public static final int LIGHT_TYPE_INPUT_SINGLE = 9;
/**
* Type for lights that indicate a group of LED lights representing player ID.
*/
public static final int LIGHT_TYPE_INPUT_PLAYER_ID = 10;
/**
* Type for lights that indicate a color LED light.
*/
public static final int LIGHT_TYPE_INPUT_RGB = 11;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"LIGHT_TYPE_"},
value = {
LIGHT_TYPE_INPUT_PLAYER_ID,
LIGHT_TYPE_INPUT_SINGLE,
LIGHT_TYPE_INPUT_RGB,
})
public @interface LightType {}
private final int mId;
private final int mOrdinal;
private final int mType;
private final String mName;
/**
* Creates a new light with the given data.
@@ -39,15 +73,26 @@ public final class Light implements Parcelable {
* @hide
*/
public Light(int id, int ordinal, int type) {
this(id, ordinal, type, "Light");
}
/**
* Creates a new light with the given data.
*
* @hide
*/
public Light(int id, int ordinal, int type, String name) {
mId = id;
mOrdinal = ordinal;
mType = type;
mName = name;
}
private Light(@NonNull Parcel in) {
mId = in.readInt();
mOrdinal = in.readInt();
mType = in.readInt();
mName = in.readString();
}
/** Implement the Parcelable interface */
@@ -56,6 +101,7 @@ public final class Light implements Parcelable {
dest.writeInt(mId);
dest.writeInt(mOrdinal);
dest.writeInt(mType);
dest.writeString(mName);
}
/** Implement the Parcelable interface */
@@ -99,6 +145,14 @@ public final class Light implements Parcelable {
return mId;
}
/**
* Returns the name of the light.
*/
@NonNull
public String getName() {
return mName;
}
/**
* Returns the ordinal of the light.
*

View File

@@ -32,36 +32,93 @@ import android.os.Parcelable;
* will be converted to only a brightness value and that will be used for the light's single
* channel.
*
* @hide
*/
@SystemApi
public final class LightState implements Parcelable {
private final int mColor;
private final int mPlayerId;
/**
* Creates a new LightState with the desired color and intensity.
* Creates a new LightState with the desired color and intensity, for a light type
* of RBG color or monochrome color.
*
* @param color the desired color and intensity in ARGB format.
* @deprecated this has been replaced with {@link android.hardware.lights.LightState#forColor }
* @hide
*/
@Deprecated
@SystemApi
public LightState(@ColorInt int color) {
mColor = color;
}
private LightState(@NonNull Parcel in) {
mColor = in.readInt();
this(color, 0);
}
/**
* Return the color and intensity associated with this LightState.
* @return the color and intensity in ARGB format. The A channel is ignored.
* Creates a new LightState with the desired color and intensity, and the player Id.
* Player Id will only be applied on Light type
* {@link android.hardware.lights.Light#LIGHT_TYPE_INPUT_PLAYER_ID}
*
* @param color the desired color and intensity in ARGB format.
* @hide
*/
public LightState(@ColorInt int color, int playerId) {
mColor = color;
mPlayerId = playerId;
}
/**
* Creates a new LightState with the desired color and intensity, for a light type
* of RBG color or single monochrome color.
*
* @param color the desired color and intensity in ARGB format.
* @return The LightState object contains the color.
*/
@NonNull
public static LightState forColor(@ColorInt int color) {
return new LightState(color, 0);
}
/**
* Creates a new LightState with the desired player id, for a light of type
* {@link android.hardware.lights.Light#LIGHT_TYPE_INPUT_PLAYER_ID}.
*
* @param playerId the desired player id.
* @return The LightState object contains the player id.
*/
@NonNull
public static LightState forPlayerId(int playerId) {
return new LightState(0, playerId);
}
/**
* Creates a new LightState from a parcel object.
*/
private LightState(@NonNull Parcel in) {
mColor = in.readInt();
mPlayerId = in.readInt();
}
/**
* Returns the color and intensity associated with this LightState.
* @return the color and intensity in ARGB format. The A channel is ignored. return 0 when
* calling LightsManager.getLightState with LIGHT_TYPE_INPUT_PLAYER_ID.
*/
public @ColorInt int getColor() {
return mColor;
}
/**
* Returns the player ID associated with this LightState for Light type
* {@link android.hardware.lights.Light#LIGHT_TYPE_INPUT_PLAYER_ID},
* or 0 for other types.
* @return the player ID.
*/
public int getPlayerId() {
return mPlayerId;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mColor);
dest.writeInt(mPlayerId);
}
@Override
@@ -69,6 +126,12 @@ public final class LightState implements Parcelable {
return 0;
}
@Override
public String toString() {
return "LightState{Color=0x" + Integer.toHexString(mColor) + ", PlayerId="
+ mPlayerId + "}";
}
public static final @NonNull Parcelable.Creator<LightState> CREATOR =
new Parcelable.Creator<LightState>() {
public LightState createFromParcel(Parcel in) {

View File

@@ -16,43 +16,38 @@
package android.hardware.lights;
import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.util.CloseGuard;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.Reference;
import java.util.List;
/**
* The LightsManager class allows control over device lights.
*
* @hide
*/
@SystemApi
@SystemService(Context.LIGHTS_SERVICE)
public final class LightsManager {
public abstract class LightsManager {
private static final String TAG = "LightsManager";
@NonNull private final Context mContext;
// These enum values copy the values from {@link com.android.server.lights.LightsManager}
// and the light HAL. Since 0-7 are lights reserved for system use, only the microphone light
// is available through this API.
/** Type for lights that indicate microphone usage */
// and following types are available through this API.
/** Type for lights that indicate microphone usage
* @deprecated this has been moved to {@link android.hardware.lights.Light }
* @hide
*/
@Deprecated
@SystemApi
public static final int LIGHT_TYPE_MICROPHONE = 8;
/** @hide */
@@ -63,28 +58,11 @@ public final class LightsManager {
})
public @interface LightType {}
@NonNull private final Context mContext;
@NonNull private final ILightsManager mService;
/**
* Creates a LightsManager.
*
* @hide
* @hide to prevent subclassing from outside of the framework
*/
public LightsManager(@NonNull Context context) throws ServiceNotFoundException {
this(context, ILightsManager.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.LIGHTS_SERVICE)));
}
/**
* Creates a LightsManager with a provided service implementation.
*
* @hide
*/
@VisibleForTesting
public LightsManager(@NonNull Context context, @NonNull ILightsManager service) {
public LightsManager(Context context) {
mContext = Preconditions.checkNotNull(context);
mService = Preconditions.checkNotNull(service);
}
/**
@@ -92,112 +70,44 @@ public final class LightsManager {
*
* @return A list of available lights
*/
@RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
public @NonNull List<Light> getLights() {
try {
return mService.getLights();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
public @NonNull abstract List<Light> getLights();
/**
* Returns the state of a specified light.
*
* @hide
*/
@RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
@TestApi
public @NonNull LightState getLightState(@NonNull Light light) {
Preconditions.checkNotNull(light);
try {
return mService.getLightState(light.getId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
public abstract @NonNull LightState getLightState(@NonNull Light light);
/**
* Creates a new LightsSession that can be used to control the device lights.
*/
@RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
public @NonNull LightsSession openSession() {
try {
final LightsSession session = new LightsSession();
mService.openSession(session.mToken);
return session;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
public abstract @NonNull LightsSession openSession();
/**
* Encapsulates a session that can be used to control device lights and represents the lifetime
* of the requests.
*/
public final class LightsSession implements AutoCloseable {
public abstract static class LightsSession implements AutoCloseable {
private final IBinder mToken = new Binder();
private final CloseGuard mCloseGuard = new CloseGuard();
private boolean mClosed = false;
/**
* Instantiated by {@link LightsManager#openSession()}.
*/
@RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
private LightsSession() {
mCloseGuard.open("close");
}
/**
* Sends a request to modify the states of multiple lights.
*
* <p>This method only controls lights that aren't overridden by higher-priority sessions.
* Additionally, lights not controlled by this session can be controlled by lower-priority
* sessions.
*
* @param request the settings for lights that should change
*/
@RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
public void requestLights(@NonNull LightsRequest request) {
Preconditions.checkNotNull(request);
if (!mClosed) {
try {
mService.setLightStates(mToken, request.mLightIds, request.mLightStates);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
public abstract void requestLights(@NonNull LightsRequest request);
@Override
public abstract void close();
/**
* Closes the session, reverting all changes made through it.
* Get the token of a light session.
*
* @return Binder token of the light session.
* @hide
*/
@RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
@Override
public void close() {
if (!mClosed) {
try {
mService.closeSession(mToken);
mClosed = true;
mCloseGuard.close();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
Reference.reachabilityFence(this);
}
/** @hide */
@Override
protected void finalize() throws Throwable {
try {
mCloseGuard.warnIfOpen();
close();
} finally {
super.finalize();
}
public @NonNull IBinder getToken() {
return mToken;
}
}
}

View File

@@ -17,17 +17,17 @@
package android.hardware.lights;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.util.SparseArray;
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Encapsulates a request to modify the state of multiple lights.
*
* @hide
*/
@SystemApi
public final class LightsRequest {
/** Visible to {@link LightsManager.Session}. */
@@ -49,6 +49,30 @@ public final class LightsRequest {
}
}
/**
* Get a list of Light as ids. The ids will returned in same order as the lights passed
* in Builder.
*
* @return List of light ids
*/
public @NonNull List<Integer> getLights() {
List<Integer> lightList = new ArrayList<Integer>(mLightIds.length);
for (int i = 0; i < mLightIds.length; i++) {
lightList.add(mLightIds[i]);
}
return lightList;
}
/**
* Get a list of LightState. The states will be returned in same order as the light states
* passed in Builder.
*
* @return List of light states
*/
public @NonNull List<LightState> getLightStates() {
return Arrays.asList(mLightStates);
}
/**
* Builder for creating device light change requests.
*/
@@ -62,7 +86,7 @@ public final class LightsRequest {
* @param light the light to modify
* @param state the desired color and intensity of the light
*/
public @NonNull Builder setLight(@NonNull Light light, @NonNull LightState state) {
public @NonNull Builder addLight(@NonNull Light light, @NonNull LightState state) {
Preconditions.checkNotNull(light);
Preconditions.checkNotNull(state);
mChanges.put(light.getId(), state);

View File

@@ -0,0 +1,181 @@
/*
* Copyright (C) 2021 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.lights;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.content.Context;
import android.hardware.lights.LightsManager.LightsSession;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.util.CloseGuard;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.lang.ref.Reference;
import java.util.List;
/**
* The LightsManager class allows control over device lights.
*
* @hide
*/
public final class SystemLightsManager extends LightsManager {
private static final String TAG = "LightsManager";
@NonNull private final ILightsManager mService;
/**
* Creates a SystemLightsManager.
*
* @hide
*/
public SystemLightsManager(@NonNull Context context) throws ServiceNotFoundException {
this(context, ILightsManager.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.LIGHTS_SERVICE)));
}
/**
* Creates a SystemLightsManager with a provided service implementation.
*
* @hide
*/
@VisibleForTesting
public SystemLightsManager(@NonNull Context context, @NonNull ILightsManager service) {
super(context);
mService = Preconditions.checkNotNull(service);
}
/**
* Returns the lights available on the device.
*
* @return A list of available lights
*/
@RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
@Override
public @NonNull List<Light> getLights() {
try {
return mService.getLights();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Returns the state of a specified light.
*
* @hide
*/
@RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
@Override
public @NonNull LightState getLightState(@NonNull Light light) {
Preconditions.checkNotNull(light);
try {
return mService.getLightState(light.getId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Creates a new LightsSession that can be used to control the device lights.
*/
@RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
@Override
public @NonNull LightsSession openSession() {
try {
final LightsSession session = new SystemLightsSession();
mService.openSession(session.getToken());
return session;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Encapsulates a session that can be used to control device lights and represents the lifetime
* of the requests.
*/
public final class SystemLightsSession extends LightsManager.LightsSession
implements AutoCloseable {
private final CloseGuard mCloseGuard = new CloseGuard();
private boolean mClosed = false;
/**
* Instantiated by {@link LightsManager#openSession()}.
*/
@RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
private SystemLightsSession() {
mCloseGuard.open("close");
}
/**
* Sends a request to modify the states of multiple lights.
*
* <p>This method only controls lights that aren't overridden by higher-priority sessions.
* Additionally, lights not controlled by this session can be controlled by lower-priority
* sessions.
*
* @param request the settings for lights that should change
*/
@RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
@Override
public void requestLights(@NonNull LightsRequest request) {
Preconditions.checkNotNull(request);
if (!mClosed) {
try {
mService.setLightStates(getToken(), request.mLightIds, request.mLightStates);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
/**
* Closes the session, reverting all changes made through it.
*/
@RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
@Override
public void close() {
if (!mClosed) {
try {
mService.closeSession(getToken());
mClosed = true;
mCloseGuard.close();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
Reference.reachabilityFence(this);
}
/** @hide */
@Override
protected void finalize() throws Throwable {
try {
mCloseGuard.warnIfOpen();
close();
} finally {
super.finalize();
}
}
}
}