blob: 27abd1e8216d202ea52b3526690a55ba5b750ad1 [file] [log] [blame]
Justin Klaassen911e8892016-06-21 18:24:24 -07001/*
2 * Copyright (C) 2016 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
17package com.android.server.display;
18
19import android.app.AlarmManager;
20import android.content.BroadcastReceiver;
21import android.content.ContentResolver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.os.Handler;
26import android.os.Looper;
27import android.os.UserHandle;
28import android.provider.Settings.Secure;
29import android.util.Slog;
30
31import com.android.internal.app.NightDisplayController;
32import com.android.server.SystemService;
33import com.android.server.twilight.TwilightListener;
34import com.android.server.twilight.TwilightManager;
35import com.android.server.twilight.TwilightState;
36
37import java.util.Calendar;
38import java.util.TimeZone;
39
40/**
41 * Tints the display at night.
42 */
43public final class NightDisplayService extends SystemService
44 implements NightDisplayController.Callback {
45
46 private static final String TAG = "NightDisplayService";
47 private static final boolean DEBUG = false;
48
49 /**
50 * Night mode ~= 3400 K.
51 */
52 private static final String MATRIX_NIGHT = "1,0,0,0,0,.754,0,0,0,0,.516,0,0,0,0,1";
53
54 private int mCurrentUser = UserHandle.USER_NULL;
55 private boolean mBootCompleted;
56
57 private NightDisplayController mController;
58 private Boolean mIsActivated;
59 private AutoMode mAutoMode;
60
61 public NightDisplayService(Context context) {
62 super(context);
63 }
64
65 @Override
66 public void onStart() {
67 // Nothing to publish.
68 }
69
70 @Override
71 public void onStartUser(int userHandle) {
72 super.onStartUser(userHandle);
73
74 // Register listeners for the new user.
75 if (mCurrentUser == UserHandle.USER_NULL) {
76 mCurrentUser = userHandle;
77 if (mBootCompleted) {
78 setUpNightMode();
79 }
80 }
81 }
82
83 @Override
84 public void onSwitchUser(int userHandle) {
85 super.onSwitchUser(userHandle);
86
87 // Unregister listeners for the old user.
88 if (mBootCompleted && mCurrentUser != UserHandle.USER_NULL) {
89 tearDownNightMode();
90 }
91
92 // Register listeners for the new user.
93 mCurrentUser = userHandle;
94 if (mBootCompleted) {
95 setUpNightMode();
96 }
97 }
98
99 @Override
100 public void onStopUser(int userHandle) {
101 super.onStopUser(userHandle);
102
103 // Unregister listeners for the old user.
104 if (mCurrentUser == userHandle) {
105 if (mBootCompleted) {
106 tearDownNightMode();
107 }
108 mCurrentUser = UserHandle.USER_NULL;
109 }
110 }
111
112 @Override
113 public void onBootPhase(int phase) {
114 if (phase == PHASE_BOOT_COMPLETED) {
115 mBootCompleted = true;
116
117 // Register listeners now that boot is complete.
118 if (mCurrentUser != UserHandle.USER_NULL) {
119 setUpNightMode();
120 }
121 }
122 }
123
124 private void setUpNightMode() {
125 // Create a new controller for the current user and start listening for changes.
126 mController = new NightDisplayController(getContext(), mCurrentUser);
127 mController.setListener(this);
128
129 // Initialize the current auto mode.
130 onAutoModeChanged(mController.getAutoMode());
131
132 // Force the initialization current activated state.
133 if (mIsActivated == null) {
134 onActivated(mController.isActivated());
135 }
136 }
137
138 private void tearDownNightMode() {
139 mController.setListener(null);
140
141 if (mAutoMode != null) {
142 mAutoMode.onStop();
143 mAutoMode = null;
144 }
145
146 mIsActivated = null;
147 mController = null;
148 }
149
150 @Override
151 public void onActivated(boolean activated) {
152 if (mIsActivated == null || mIsActivated != activated) {
153 Slog.i(TAG, activated ? "Turning on night display" : "Turning off night display");
154
155 mIsActivated = activated;
156
157 if (mAutoMode != null) {
158 mAutoMode.onActivated(activated);
159 }
160
161 // Update the current color matrix.
162 final ContentResolver cr = getContext().getContentResolver();
163 Secure.putStringForUser(cr, Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX,
164 activated ? MATRIX_NIGHT : null, mCurrentUser);
165 }
166 }
167
168 @Override
169 public void onAutoModeChanged(int autoMode) {
170 if (mAutoMode != null) {
171 mAutoMode.onStop();
172 mAutoMode = null;
173 }
174
175 if (autoMode == NightDisplayController.AUTO_MODE_CUSTOM) {
176 mAutoMode = new CustomAutoMode();
177 } else if (autoMode == NightDisplayController.AUTO_MODE_TWILIGHT) {
178 mAutoMode = new TwilightAutoMode();
179 }
180
181 if (mAutoMode != null) {
182 mAutoMode.onStart();
183 }
184 }
185
186 @Override
187 public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) {
188 if (mAutoMode != null) {
189 mAutoMode.onCustomStartTimeChanged(startTime);
190 }
191 }
192
193 @Override
194 public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) {
195 if (mAutoMode != null) {
196 mAutoMode.onCustomEndTimeChanged(endTime);
197 }
198 }
199
200 private abstract class AutoMode implements NightDisplayController.Callback {
201 public abstract void onStart();
202 public abstract void onStop();
203 }
204
205 private class CustomAutoMode extends AutoMode implements AlarmManager.OnAlarmListener {
206
207 private final AlarmManager mAlarmManager;
208 private final BroadcastReceiver mTimeChangedReceiver;
209
210 private NightDisplayController.LocalTime mStartTime;
211 private NightDisplayController.LocalTime mEndTime;
212
213 private Calendar mLastActivatedTime;
214
215 public CustomAutoMode() {
216 mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
217 mTimeChangedReceiver = new BroadcastReceiver() {
218 @Override
219 public void onReceive(Context context, Intent intent) {
220 updateActivated();
221 }
222 };
223 }
224
225 private void updateActivated() {
226 final Calendar now = Calendar.getInstance();
227 final Calendar startTime = mStartTime.getDateTimeBefore(now);
228 final Calendar endTime = mEndTime.getDateTimeAfter(startTime);
229 final boolean activated = now.before(endTime);
230
231 boolean setActivated = mIsActivated == null || mLastActivatedTime == null;
232 if (!setActivated && mIsActivated != activated) {
233 final TimeZone currentTimeZone = now.getTimeZone();
234 if (!currentTimeZone.equals(mLastActivatedTime.getTimeZone())) {
235 final int year = mLastActivatedTime.get(Calendar.YEAR);
236 final int dayOfYear = mLastActivatedTime.get(Calendar.DAY_OF_YEAR);
237 final int hourOfDay = mLastActivatedTime.get(Calendar.HOUR_OF_DAY);
238 final int minute = mLastActivatedTime.get(Calendar.MINUTE);
239
240 mLastActivatedTime.setTimeZone(currentTimeZone);
241 mLastActivatedTime.set(Calendar.YEAR, year);
242 mLastActivatedTime.set(Calendar.DAY_OF_YEAR, dayOfYear);
243 mLastActivatedTime.set(Calendar.HOUR_OF_DAY, hourOfDay);
244 mLastActivatedTime.set(Calendar.MINUTE, minute);
245 }
246
247 if (mIsActivated) {
248 setActivated = now.before(mStartTime.getDateTimeBefore(mLastActivatedTime))
249 || now.after(mEndTime.getDateTimeAfter(mLastActivatedTime));
250 } else {
251 setActivated = now.before(mEndTime.getDateTimeBefore(mLastActivatedTime))
252 || now.after(mStartTime.getDateTimeAfter(mLastActivatedTime));
253 }
254 }
255
256 if (setActivated) {
257 mController.setActivated(activated);
258 }
259 updateNextAlarm();
260 }
261
262 private void updateNextAlarm() {
263 if (mIsActivated != null) {
264 final Calendar now = Calendar.getInstance();
265 final Calendar next = mIsActivated ? mEndTime.getDateTimeAfter(now)
266 : mStartTime.getDateTimeAfter(now);
267 mAlarmManager.setExact(AlarmManager.RTC, next.getTimeInMillis(), TAG, this, null);
268 }
269 }
270
271 @Override
272 public void onStart() {
273 final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_TIME_CHANGED);
274 intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
275 getContext().registerReceiver(mTimeChangedReceiver, intentFilter);
276
277 mStartTime = mController.getCustomStartTime();
278 mEndTime = mController.getCustomEndTime();
279
280 // Force an update to initialize state.
281 updateActivated();
282 }
283
284 @Override
285 public void onStop() {
286 getContext().unregisterReceiver(mTimeChangedReceiver);
287
288 mAlarmManager.cancel(this);
289 mLastActivatedTime = null;
290 }
291
292 @Override
293 public void onActivated(boolean activated) {
294 mLastActivatedTime = Calendar.getInstance();
295 updateNextAlarm();
296 }
297
298 @Override
299 public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) {
300 mStartTime = startTime;
301 mLastActivatedTime = null;
302 updateActivated();
303 }
304
305 @Override
306 public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) {
307 mEndTime = endTime;
308 mLastActivatedTime = null;
309 updateActivated();
310 }
311
312 @Override
313 public void onAlarm() {
314 if (DEBUG) Slog.d(TAG, "onAlarm");
315 updateActivated();
316 }
317 }
318
319 private class TwilightAutoMode extends AutoMode implements TwilightListener {
320
321 private final TwilightManager mTwilightManager;
322 private final Handler mHandler;
323
324 private boolean mIsNight;
325
326 public TwilightAutoMode() {
327 mTwilightManager = getLocalService(TwilightManager.class);
328 mHandler = new Handler(Looper.getMainLooper());
329 }
330
331 private void updateActivated() {
332 final TwilightState state = mTwilightManager.getCurrentState();
333 final boolean isNight = state != null && state.isNight();
334 if (mIsNight != isNight) {
335 mIsNight = isNight;
336
337 if (mIsActivated == null || mIsActivated != isNight) {
338 mController.setActivated(isNight);
339 }
340 }
341 }
342
343 @Override
344 public void onStart() {
345 mTwilightManager.registerListener(this, mHandler);
346
347 // Force an update to initialize state.
348 updateActivated();
349 }
350
351 @Override
352 public void onStop() {
353 mTwilightManager.unregisterListener(this);
354 }
355
356 @Override
357 public void onTwilightStateChanged() {
358 if (DEBUG) Slog.d(TAG, "onTwilightStateChanged");
359 updateActivated();
360 }
361 }
362}