1/* * Copyright (C) 2008 The Android Open Source Project
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *      http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16package com.android.server.lights;
17
18import com.android.server.SystemService;
19
20import android.app.ActivityManager;
21import android.content.Context;
22import android.os.Handler;
23import android.os.Message;
24import android.os.Trace;
25import android.provider.Settings;
26import android.util.Slog;
27
28public class LightsService extends SystemService {
29    static final String TAG = "LightsService";
30    static final boolean DEBUG = false;
31
32    final LightImpl mLights[] = new LightImpl[LightsManager.LIGHT_ID_COUNT];
33
34    private final class LightImpl extends Light {
35
36        private LightImpl(int id) {
37            mId = id;
38        }
39
40        @Override
41        public void setBrightness(int brightness) {
42            setBrightness(brightness, BRIGHTNESS_MODE_USER);
43        }
44
45        @Override
46        public void setBrightness(int brightness, int brightnessMode) {
47            synchronized (this) {
48                // LOW_PERSISTENCE cannot be manually set
49                if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) {
50                    Slog.w(TAG, "setBrightness with LOW_PERSISTENCE unexpected #" + mId +
51                            ": brightness=0x" + Integer.toHexString(brightness));
52                    return;
53                }
54
55                int color = brightness & 0x000000ff;
56                color = 0xff000000 | (color << 16) | (color << 8) | color;
57                setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode);
58            }
59        }
60
61        @Override
62        public void setColor(int color) {
63            synchronized (this) {
64                setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, 0);
65            }
66        }
67
68        @Override
69        public void setFlashing(int color, int mode, int onMS, int offMS) {
70            synchronized (this) {
71                setLightLocked(color, mode, onMS, offMS, BRIGHTNESS_MODE_USER);
72            }
73        }
74
75        @Override
76        public void pulse() {
77            pulse(0x00ffffff, 7);
78        }
79
80        @Override
81        public void pulse(int color, int onMS) {
82            synchronized (this) {
83                if (mColor == 0 && !mFlashing) {
84                    setLightLocked(color, LIGHT_FLASH_HARDWARE, onMS, 1000,
85                            BRIGHTNESS_MODE_USER);
86                    mColor = 0;
87                    mH.sendMessageDelayed(Message.obtain(mH, 1, this), onMS);
88                }
89            }
90        }
91
92        @Override
93        public void turnOff() {
94            synchronized (this) {
95                setLightLocked(0, LIGHT_FLASH_NONE, 0, 0, 0);
96            }
97        }
98
99        @Override
100        public void setVrMode(boolean enabled) {
101            synchronized (this) {
102                if (mVrModeEnabled != enabled) {
103                    mVrModeEnabled = enabled;
104
105                    mUseLowPersistenceForVR =
106                            (getVrDisplayMode() == Settings.Secure.VR_DISPLAY_MODE_LOW_PERSISTENCE);
107                    if (shouldBeInLowPersistenceMode()) {
108                        mLastBrightnessMode = mBrightnessMode;
109                    }
110
111                    // NOTE: We do not trigger a call to setLightLocked here.  We do not know the
112                    // current brightness or other values when leaving VR so we avoid any incorrect
113                    // jumps. The code that calls this method will immediately issue a brightness
114                    // update which is when the change will occur.
115                }
116            }
117        }
118
119        private void stopFlashing() {
120            synchronized (this) {
121                setLightLocked(mColor, LIGHT_FLASH_NONE, 0, 0, BRIGHTNESS_MODE_USER);
122            }
123        }
124
125        private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) {
126            if (shouldBeInLowPersistenceMode()) {
127                brightnessMode = BRIGHTNESS_MODE_LOW_PERSISTENCE;
128            } else if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) {
129                brightnessMode = mLastBrightnessMode;
130            }
131
132            if (!mInitialized || color != mColor || mode != mMode || onMS != mOnMS ||
133                    offMS != mOffMS || mBrightnessMode != brightnessMode) {
134                if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#"
135                        + Integer.toHexString(color) + ": brightnessMode=" + brightnessMode);
136                mInitialized = true;
137                mLastColor = mColor;
138                mColor = color;
139                mMode = mode;
140                mOnMS = onMS;
141                mOffMS = offMS;
142                mBrightnessMode = brightnessMode;
143                Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLight(" + mId + ", 0x"
144                        + Integer.toHexString(color) + ")");
145                try {
146                    setLight_native(mId, color, mode, onMS, offMS, brightnessMode);
147                } finally {
148                    Trace.traceEnd(Trace.TRACE_TAG_POWER);
149                }
150            }
151        }
152
153        private boolean shouldBeInLowPersistenceMode() {
154            return mVrModeEnabled && mUseLowPersistenceForVR;
155        }
156
157        private int mId;
158        private int mColor;
159        private int mMode;
160        private int mOnMS;
161        private int mOffMS;
162        private boolean mFlashing;
163        private int mBrightnessMode;
164        private int mLastBrightnessMode;
165        private int mLastColor;
166        private boolean mVrModeEnabled;
167        private boolean mUseLowPersistenceForVR;
168        private boolean mInitialized;
169    }
170
171    public LightsService(Context context) {
172        super(context);
173
174        for (int i = 0; i < LightsManager.LIGHT_ID_COUNT; i++) {
175            mLights[i] = new LightImpl(i);
176        }
177    }
178
179    @Override
180    public void onStart() {
181        publishLocalService(LightsManager.class, mService);
182    }
183
184    @Override
185    public void onBootPhase(int phase) {
186    }
187
188    private int getVrDisplayMode() {
189        int currentUser = ActivityManager.getCurrentUser();
190        return Settings.Secure.getIntForUser(getContext().getContentResolver(),
191                Settings.Secure.VR_DISPLAY_MODE,
192                /*default*/Settings.Secure.VR_DISPLAY_MODE_LOW_PERSISTENCE,
193                currentUser);
194    }
195
196    private final LightsManager mService = new LightsManager() {
197        @Override
198        public Light getLight(int id) {
199            if (0 <= id && id < LIGHT_ID_COUNT) {
200                return mLights[id];
201            } else {
202                return null;
203            }
204        }
205    };
206
207    private Handler mH = new Handler() {
208        @Override
209        public void handleMessage(Message msg) {
210            LightImpl light = (LightImpl)msg.obj;
211            light.stopFlashing();
212        }
213    };
214
215    static native void setLight_native(int light, int color, int mode,
216            int onMS, int offMS, int brightnessMode);
217}
218