Add LightsManager implementation to LightsService

Includes backend code to support LightsManager binder calls and route
them to the HALs.

Bug: 144979010
Bug: 144978691
Bug: 142715294
Change-Id: I0080972620ba7a3fb1197cdd0288287d3cfa8780
Fix: 142230898
Test: atest LightsManagerTest
Test: atest LightsServiceTest
Merged-In: I2db7f2caa432cd1e2389ea5ca6544200ada18675
This commit is contained in:
Ivailo Karamanolev
2020-01-09 17:02:49 +01:00
committed by Robin Lee
parent 4c5ac6b353
commit 3e376b79ce
14 changed files with 1068 additions and 14 deletions

View File

@@ -83,6 +83,7 @@ import android.hardware.hdmi.IHdmiControlService;
import android.hardware.input.InputManager;
import android.hardware.iris.IIrisService;
import android.hardware.iris.IrisManager;
import android.hardware.lights.LightsManager;
import android.hardware.location.ContextHubManager;
import android.hardware.radio.RadioManager;
import android.hardware.usb.IUsbManager;
@@ -1348,6 +1349,13 @@ final class SystemServiceRegistry {
return new DynamicSystemManager(
IDynamicSystemService.Stub.asInterface(b));
}});
registerService(Context.LIGHTS_SERVICE, LightsManager.class,
new CachedServiceFetcher<LightsManager>() {
@Override
public LightsManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
return new LightsManager(ctx);
}});
//CHECKSTYLE:ON IndentationCheck
}

View File

@@ -3380,6 +3380,7 @@ public abstract class Context {
//@hide: TIME_DETECTOR_SERVICE,
//@hide: TIME_ZONE_DETECTOR_SERVICE,
PERMISSION_SERVICE,
LIGHTS_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -4879,6 +4880,15 @@ public abstract class Context {
@SystemApi
public static final String TELEPHONY_REGISTRY_SERVICE = "telephony_registry";
/**
* Use with {@link #getSystemService(String)} to retrieve a
* {@link android.hardware.lights.LightsManager} for controlling device lights.
*
* @see #getSystemService(String)
* @hide
*/
public static final String LIGHTS_SERVICE = "lights";
/**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.

View File

@@ -0,0 +1,33 @@
/**
* 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.lights;
import android.hardware.lights.Light;
import android.hardware.lights.LightState;
/**
* API to lights manager service.
*
* {@hide}
*/
interface ILightsManager {
List<Light> getLights();
LightState getLightState(int lightId);
void openSession(in IBinder sessionToken);
void closeSession(in IBinder sessionToken);
void setLightStates(in IBinder sessionToken, in int[] lightIds, in LightState[] states);
}

View File

@@ -0,0 +1,20 @@
/**
* 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.lights;
/** @hide */
parcelable Light;

View File

@@ -0,0 +1,105 @@
/**
* 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.lights;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Represents a logical light on the device.
*
* @hide
*/
@SystemApi
@TestApi
public final class Light implements Parcelable {
private final int mId;
private final int mOrdinal;
private final int mType;
/**
* Creates a new light with the given data.
*
* @hide */
public Light(int id, int ordinal, int type) {
mId = id;
mOrdinal = ordinal;
mType = type;
}
private Light(@NonNull Parcel in) {
mId = in.readInt();
mOrdinal = in.readInt();
mType = in.readInt();
}
/** Implement the Parcelable interface */
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mId);
dest.writeInt(mOrdinal);
dest.writeInt(mType);
}
/** Implement the Parcelable interface */
@Override
public int describeContents() {
return 0;
}
/** Implement the Parcelable interface */
public static final @android.annotation.NonNull Parcelable.Creator<Light> CREATOR =
new Parcelable.Creator<Light>() {
public Light createFromParcel(Parcel in) {
return new Light(in);
}
public Light[] newArray(int size) {
return new Light[size];
}
};
/**
* Returns the id of the light.
*/
public int getId() {
return mId;
}
/**
* Returns the ordinal of the light.
*
* <p>This represents the physical order of the lights on the device. The exact values are
* device-dependent, but for example, if there are lights in a row, sorting the Light objects
* by ordinal should match the order in which they appear on the device. If the device has
* 4 lights, the ordinals could be [1, 2, 3, 4] or [0, 10, 20, 30] or any other values that
* have the same sort order.
*/
public int getOrdinal() {
return mOrdinal;
}
/**
* Returns the logical type of the light.
*/
public @LightsManager.LightType int getType() {
return mType;
}
}

View File

@@ -0,0 +1,20 @@
/**
* 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.lights;
/** @hide */
parcelable LightState;

View File

@@ -0,0 +1,84 @@
/**
* 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.lights;
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Represents the state of a device light.
*
* <p>Controlling the color and brightness of a light is done on a best-effort basis. Each of the R,
* G and B channels represent the intensities of the respective part of an RGB LED, if that is
* supported. For devices that only support on or off lights, everything that's not off will turn
* the light on. If the light is monochrome and only the brightness can be controlled, the RGB color
* will be converted to only a brightness value and that will be used for the light's single
* channel.
*
* @hide
*/
@SystemApi
@TestApi
public final class LightState implements Parcelable {
private final int mColor;
/**
* Creates a new LightState with the desired color and intensity.
*
* @param color the desired color and intensity in ARGB format.
*/
public LightState(@ColorInt int color) {
mColor = color;
}
private LightState(@NonNull Parcel in) {
mColor = in.readInt();
}
/**
* Return the color and intensity associated with this LightState.
* @return the color and intensity in ARGB format. The A channel is ignored.
*/
public @ColorInt int getColor() {
return mColor;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mColor);
}
@Override
public int describeContents() {
return 0;
}
public static final @NonNull Parcelable.Creator<LightState> CREATOR =
new Parcelable.Creator<LightState>() {
public LightState createFromParcel(Parcel in) {
return new LightState(in);
}
public LightState[] newArray(int size) {
return new LightState[size];
}
};
}

View File

@@ -0,0 +1,204 @@
/*
* 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.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
@TestApi
@SystemService(Context.LIGHTS_SERVICE)
public final class LightsManager {
private static final String TAG = "LightsManager";
// 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 */
public static final int LIGHT_TYPE_MICROPHONE = 8;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"LIGHT_TYPE_"},
value = {
LIGHT_TYPE_MICROPHONE,
})
public @interface LightType {}
@NonNull private final Context mContext;
@NonNull private final ILightsManager mService;
/**
* Creates a LightsManager.
*
* @hide
*/
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) {
mContext = Preconditions.checkNotNull(context);
mService = Preconditions.checkNotNull(service);
}
/**
* Returns the lights available on the device.
*
* @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();
}
}
/**
* 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();
}
}
/**
* 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();
}
}
/**
* Encapsulates a session that can be used to control device lights and represents the lifetime
* of the requests.
*/
public final 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 setLights(@NonNull LightsRequest request) {
Preconditions.checkNotNull(request);
if (!mClosed) {
try {
mService.setLightStates(mToken, 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(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();
}
}
}
}

View File

@@ -0,0 +1,95 @@
/*
* 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.lights;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.util.SparseArray;
import com.android.internal.util.Preconditions;
/**
* Encapsulates a request to modify the state of multiple lights.
*
* @hide
*/
@SystemApi
@TestApi
public final class LightsRequest {
/** Visible to {@link LightsManager.Session}. */
final int[] mLightIds;
/** Visible to {@link LightsManager.Session}. */
final LightState[] mLightStates;
/**
* Can only be constructed via {@link LightsRequest.Builder#build()}.
*/
private LightsRequest(SparseArray<LightState> changes) {
final int n = changes.size();
mLightIds = new int[n];
mLightStates = new LightState[n];
for (int i = 0; i < n; i++) {
mLightIds[i] = changes.keyAt(i);
mLightStates[i] = changes.valueAt(i);
}
}
/**
* Builder for creating device light change requests.
*/
public static final class Builder {
private final SparseArray<LightState> mChanges = new SparseArray<>();
/**
* Overrides the color and intensity of a given light.
*
* @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) {
Preconditions.checkNotNull(light);
Preconditions.checkNotNull(state);
mChanges.put(light.getId(), state);
return this;
}
/**
* Removes the override for the color and intensity of a given light.
*
* @param light the light to modify
*/
public @NonNull Builder clearLight(@NonNull Light light) {
Preconditions.checkNotNull(light);
mChanges.put(light.getId(), null);
return this;
}
/**
* Create a LightsRequest object used to override lights on the device.
*
* <p>The generated {@link LightsRequest} should be used in
* {@link LightsManager.Session#setLights(LightsLightsRequest).
*/
public @NonNull LightsRequest build() {
return new LightsRequest(mChanges);
}
}
}