1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.hardware.lights;
18 
19 import android.Manifest;
20 import android.annotation.NonNull;
21 import android.annotation.RequiresPermission;
22 import android.content.Context;
23 import android.hardware.lights.LightsManager.LightsSession;
24 import android.os.RemoteException;
25 import android.os.ServiceManager;
26 import android.os.ServiceManager.ServiceNotFoundException;
27 import android.util.CloseGuard;
28 
29 import com.android.internal.annotations.VisibleForTesting;
30 import com.android.internal.util.Preconditions;
31 
32 import java.lang.ref.Reference;
33 import java.util.List;
34 
35 /**
36  * The LightsManager class allows control over device lights.
37  *
38  * @hide
39  */
40 public final class SystemLightsManager extends LightsManager {
41     private static final String TAG = "LightsManager";
42 
43     @NonNull private final ILightsManager mService;
44 
45     /**
46      * Creates a SystemLightsManager.
47      *
48      * @hide
49      */
SystemLightsManager(@onNull Context context)50     public SystemLightsManager(@NonNull Context context) throws ServiceNotFoundException {
51         this(context, ILightsManager.Stub.asInterface(
52             ServiceManager.getServiceOrThrow(Context.LIGHTS_SERVICE)));
53     }
54 
55     /**
56      * Creates a SystemLightsManager with a provided service implementation.
57      *
58      * @hide
59      */
60     @VisibleForTesting
SystemLightsManager(@onNull Context context, @NonNull ILightsManager service)61     public SystemLightsManager(@NonNull Context context, @NonNull ILightsManager service) {
62         mService = Preconditions.checkNotNull(service);
63     }
64 
65     /**
66      * Returns the lights available on the device.
67      *
68      * @return A list of available lights
69      */
70     @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
71     @Override
getLights()72     public @NonNull List<Light> getLights() {
73         try {
74             return mService.getLights();
75         } catch (RemoteException e) {
76             throw e.rethrowFromSystemServer();
77         }
78     }
79 
80     /**
81      * Returns the state of a specified light.
82      *
83      * @hide
84      */
85     @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
86     @Override
getLightState(@onNull Light light)87     public @NonNull LightState getLightState(@NonNull Light light) {
88         Preconditions.checkNotNull(light);
89         try {
90             return mService.getLightState(light.getId());
91         } catch (RemoteException e) {
92             throw e.rethrowFromSystemServer();
93         }
94     }
95 
96     /**
97      * Creates a new LightsSession that can be used to control the device lights.
98      */
99     @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
100     @Override
openSession()101     public @NonNull LightsSession openSession() {
102         try {
103             final LightsSession session = new SystemLightsSession();
104             mService.openSession(session.getToken(), 0);
105             return session;
106         } catch (RemoteException e) {
107             throw e.rethrowFromSystemServer();
108         }
109     }
110 
111     /**
112      *
113      * Creates a new {@link LightsSession}
114      *
115      * @param priority the larger this number, the higher the priority of this session when multiple
116      *                 light state requests arrive simultaneously.
117      *
118      * @hide
119      */
120     @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
121     @Override
openSession(int priority)122     public @NonNull LightsSession openSession(int priority) {
123         try {
124             final LightsSession session = new SystemLightsSession();
125             mService.openSession(session.getToken(), priority);
126             return session;
127         } catch (RemoteException e) {
128             throw e.rethrowFromSystemServer();
129         }
130     }
131 
132     /**
133      * Encapsulates a session that can be used to control device lights and represents the lifetime
134      * of the requests.
135      */
136     public final class SystemLightsSession extends LightsManager.LightsSession
137             implements AutoCloseable {
138 
139         private final CloseGuard mCloseGuard = new CloseGuard();
140         private boolean mClosed = false;
141 
142         /**
143          * Instantiated by {@link LightsManager#openSession()}.
144          */
145         @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
SystemLightsSession()146         private SystemLightsSession() {
147             mCloseGuard.open("SystemLightsSession.close");
148         }
149 
150         /**
151          * Sends a request to modify the states of multiple lights.
152          *
153          * <p>This method only controls lights that aren't overridden by higher-priority sessions.
154          * Additionally, lights not controlled by this session can be controlled by lower-priority
155          * sessions.
156          *
157          * @param request the settings for lights that should change
158          */
159         @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
160         @Override
requestLights(@onNull LightsRequest request)161         public void requestLights(@NonNull LightsRequest request) {
162             Preconditions.checkNotNull(request);
163             if (!mClosed) {
164                 try {
165                     List<Integer> idList = request.getLights();
166                     List<LightState> stateList = request.getLightStates();
167                     int[] ids = new int[idList.size()];
168                     for (int i = 0; i < idList.size(); i++) {
169                         ids[i] = idList.get(i);
170                     }
171                     LightState[] states = new LightState[stateList.size()];
172                     for (int i = 0; i < stateList.size(); i++) {
173                         states[i] = stateList.get(i);
174                     }
175                     mService.setLightStates(getToken(), ids, states);
176                 } catch (RemoteException e) {
177                     throw e.rethrowFromSystemServer();
178                 }
179             }
180         }
181 
182         /**
183          * Closes the session, reverting all changes made through it.
184          */
185         @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS)
186         @Override
close()187         public void close() {
188             if (!mClosed) {
189                 try {
190                     mService.closeSession(getToken());
191                     mClosed = true;
192                     mCloseGuard.close();
193                 } catch (RemoteException e) {
194                     throw e.rethrowFromSystemServer();
195                 }
196             }
197             Reference.reachabilityFence(this);
198         }
199 
200         /** @hide */
201         @Override
finalize()202         protected void finalize() throws Throwable {
203             try {
204                 mCloseGuard.warnIfOpen();
205                 close();
206             } finally {
207                 super.finalize();
208             }
209         }
210     }
211 }
212