• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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 com.android.server.dreams;
18 
19 import static android.Manifest.permission.BIND_DREAM_SERVICE;
20 
21 import com.android.internal.util.DumpUtils;
22 import com.android.server.FgThread;
23 import com.android.server.SystemService;
24 
25 import android.Manifest;
26 import android.app.ActivityManager;
27 import android.content.BroadcastReceiver;
28 import android.content.ComponentName;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.IntentFilter;
32 import android.content.pm.PackageManager;
33 import android.content.pm.PackageManager.NameNotFoundException;
34 import android.content.pm.ServiceInfo;
35 import android.os.Binder;
36 import android.os.Build;
37 import android.os.Handler;
38 import android.os.IBinder;
39 import android.os.Looper;
40 import android.os.PowerManager;
41 import android.os.PowerManagerInternal;
42 import android.os.SystemClock;
43 import android.os.SystemProperties;
44 import android.os.UserHandle;
45 import android.provider.Settings;
46 import android.service.dreams.DreamManagerInternal;
47 import android.service.dreams.DreamService;
48 import android.service.dreams.IDreamManager;
49 import android.text.TextUtils;
50 import android.util.Slog;
51 import android.view.Display;
52 
53 import java.io.FileDescriptor;
54 import java.io.PrintWriter;
55 import java.util.ArrayList;
56 import java.util.List;
57 
58 import libcore.util.Objects;
59 
60 /**
61  * Service api for managing dreams.
62  *
63  * @hide
64  */
65 public final class DreamManagerService extends SystemService {
66     private static final boolean DEBUG = false;
67     private static final String TAG = "DreamManagerService";
68 
69     private final Object mLock = new Object();
70 
71     private final Context mContext;
72     private final DreamHandler mHandler;
73     private final DreamController mController;
74     private final PowerManager mPowerManager;
75     private final PowerManagerInternal mPowerManagerInternal;
76     private final PowerManager.WakeLock mDozeWakeLock;
77 
78     private Binder mCurrentDreamToken;
79     private ComponentName mCurrentDreamName;
80     private int mCurrentDreamUserId;
81     private boolean mCurrentDreamIsTest;
82     private boolean mCurrentDreamCanDoze;
83     private boolean mCurrentDreamIsDozing;
84     private boolean mCurrentDreamIsWaking;
85     private int mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;
86     private int mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
87 
DreamManagerService(Context context)88     public DreamManagerService(Context context) {
89         super(context);
90         mContext = context;
91         mHandler = new DreamHandler(FgThread.get().getLooper());
92         mController = new DreamController(context, mHandler, mControllerListener);
93 
94         mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
95         mPowerManagerInternal = getLocalService(PowerManagerInternal.class);
96         mDozeWakeLock = mPowerManager.newWakeLock(PowerManager.DOZE_WAKE_LOCK, TAG);
97     }
98 
99     @Override
onStart()100     public void onStart() {
101         publishBinderService(DreamService.DREAM_SERVICE, new BinderService());
102         publishLocalService(DreamManagerInternal.class, new LocalService());
103     }
104 
105     @Override
onBootPhase(int phase)106     public void onBootPhase(int phase) {
107         if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
108             if (Build.IS_DEBUGGABLE) {
109                 SystemProperties.addChangeCallback(mSystemPropertiesChanged);
110             }
111             mContext.registerReceiver(new BroadcastReceiver() {
112                 @Override
113                 public void onReceive(Context context, Intent intent) {
114                     synchronized (mLock) {
115                         stopDreamLocked(false /*immediate*/);
116                     }
117                 }
118             }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
119         }
120     }
121 
dumpInternal(PrintWriter pw)122     private void dumpInternal(PrintWriter pw) {
123         pw.println("DREAM MANAGER (dumpsys dreams)");
124         pw.println();
125         pw.println("mCurrentDreamToken=" + mCurrentDreamToken);
126         pw.println("mCurrentDreamName=" + mCurrentDreamName);
127         pw.println("mCurrentDreamUserId=" + mCurrentDreamUserId);
128         pw.println("mCurrentDreamIsTest=" + mCurrentDreamIsTest);
129         pw.println("mCurrentDreamCanDoze=" + mCurrentDreamCanDoze);
130         pw.println("mCurrentDreamIsDozing=" + mCurrentDreamIsDozing);
131         pw.println("mCurrentDreamIsWaking=" + mCurrentDreamIsWaking);
132         pw.println("mCurrentDreamDozeScreenState="
133                 + Display.stateToString(mCurrentDreamDozeScreenState));
134         pw.println("mCurrentDreamDozeScreenBrightness=" + mCurrentDreamDozeScreenBrightness);
135         pw.println("getDozeComponent()=" + getDozeComponent());
136         pw.println();
137 
138         DumpUtils.dumpAsync(mHandler, new DumpUtils.Dump() {
139             @Override
140             public void dump(PrintWriter pw, String prefix) {
141                 mController.dump(pw);
142             }
143         }, pw, "", 200);
144     }
145 
isDreamingInternal()146     private boolean isDreamingInternal() {
147         synchronized (mLock) {
148             return mCurrentDreamToken != null && !mCurrentDreamIsTest
149                     && !mCurrentDreamIsWaking;
150         }
151     }
152 
requestDreamInternal()153     private void requestDreamInternal() {
154         // Ask the power manager to nap.  It will eventually call back into
155         // startDream() if/when it is appropriate to start dreaming.
156         // Because napping could cause the screen to turn off immediately if the dream
157         // cannot be started, we keep one eye open and gently poke user activity.
158         long time = SystemClock.uptimeMillis();
159         mPowerManager.userActivity(time, true /*noChangeLights*/);
160         mPowerManager.nap(time);
161     }
162 
requestAwakenInternal()163     private void requestAwakenInternal() {
164         // Treat an explicit request to awaken as user activity so that the
165         // device doesn't immediately go to sleep if the timeout expired,
166         // for example when being undocked.
167         long time = SystemClock.uptimeMillis();
168         mPowerManager.userActivity(time, false /*noChangeLights*/);
169         stopDreamInternal(false /*immediate*/);
170     }
171 
finishSelfInternal(IBinder token, boolean immediate)172     private void finishSelfInternal(IBinder token, boolean immediate) {
173         if (DEBUG) {
174             Slog.d(TAG, "Dream finished: " + token + ", immediate=" + immediate);
175         }
176 
177         // Note that a dream finishing and self-terminating is not
178         // itself considered user activity.  If the dream is ending because
179         // the user interacted with the device then user activity will already
180         // have been poked so the device will stay awake a bit longer.
181         // If the dream is ending on its own for other reasons and no wake
182         // locks are held and the user activity timeout has expired then the
183         // device may simply go to sleep.
184         synchronized (mLock) {
185             if (mCurrentDreamToken == token) {
186                 stopDreamLocked(immediate);
187             }
188         }
189     }
190 
testDreamInternal(ComponentName dream, int userId)191     private void testDreamInternal(ComponentName dream, int userId) {
192         synchronized (mLock) {
193             startDreamLocked(dream, true /*isTest*/, false /*canDoze*/, userId);
194         }
195     }
196 
startDreamInternal(boolean doze)197     private void startDreamInternal(boolean doze) {
198         final int userId = ActivityManager.getCurrentUser();
199         final ComponentName dream = chooseDreamForUser(doze, userId);
200         if (dream != null) {
201             synchronized (mLock) {
202                 startDreamLocked(dream, false /*isTest*/, doze, userId);
203             }
204         }
205     }
206 
stopDreamInternal(boolean immediate)207     private void stopDreamInternal(boolean immediate) {
208         synchronized (mLock) {
209             stopDreamLocked(immediate);
210         }
211     }
212 
startDozingInternal(IBinder token, int screenState, int screenBrightness)213     private void startDozingInternal(IBinder token, int screenState,
214             int screenBrightness) {
215         if (DEBUG) {
216             Slog.d(TAG, "Dream requested to start dozing: " + token
217                     + ", screenState=" + screenState
218                     + ", screenBrightness=" + screenBrightness);
219         }
220 
221         synchronized (mLock) {
222             if (mCurrentDreamToken == token && mCurrentDreamCanDoze) {
223                 mCurrentDreamDozeScreenState = screenState;
224                 mCurrentDreamDozeScreenBrightness = screenBrightness;
225                 mPowerManagerInternal.setDozeOverrideFromDreamManager(
226                         screenState, screenBrightness);
227                 if (!mCurrentDreamIsDozing) {
228                     mCurrentDreamIsDozing = true;
229                     mDozeWakeLock.acquire();
230                 }
231             }
232         }
233     }
234 
stopDozingInternal(IBinder token)235     private void stopDozingInternal(IBinder token) {
236         if (DEBUG) {
237             Slog.d(TAG, "Dream requested to stop dozing: " + token);
238         }
239 
240         synchronized (mLock) {
241             if (mCurrentDreamToken == token && mCurrentDreamIsDozing) {
242                 mCurrentDreamIsDozing = false;
243                 mDozeWakeLock.release();
244                 mPowerManagerInternal.setDozeOverrideFromDreamManager(
245                         Display.STATE_UNKNOWN, PowerManager.BRIGHTNESS_DEFAULT);
246             }
247         }
248     }
249 
chooseDreamForUser(boolean doze, int userId)250     private ComponentName chooseDreamForUser(boolean doze, int userId) {
251         if (doze) {
252             ComponentName dozeComponent = getDozeComponent(userId);
253             return validateDream(dozeComponent) ? dozeComponent : null;
254         }
255         ComponentName[] dreams = getDreamComponentsForUser(userId);
256         return dreams != null && dreams.length != 0 ? dreams[0] : null;
257     }
258 
validateDream(ComponentName component)259     private boolean validateDream(ComponentName component) {
260         if (component == null) return false;
261         final ServiceInfo serviceInfo = getServiceInfo(component);
262         if (serviceInfo == null) {
263             Slog.w(TAG, "Dream " + component + " does not exist");
264             return false;
265         } else if (serviceInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP
266                 && !BIND_DREAM_SERVICE.equals(serviceInfo.permission)) {
267             Slog.w(TAG, "Dream " + component
268                     + " is not available because its manifest is missing the " + BIND_DREAM_SERVICE
269                     + " permission on the dream service declaration.");
270             return false;
271         }
272         return true;
273     }
274 
getDreamComponentsForUser(int userId)275     private ComponentName[] getDreamComponentsForUser(int userId) {
276         String names = Settings.Secure.getStringForUser(mContext.getContentResolver(),
277                 Settings.Secure.SCREENSAVER_COMPONENTS,
278                 userId);
279         ComponentName[] components = componentsFromString(names);
280 
281         // first, ensure components point to valid services
282         List<ComponentName> validComponents = new ArrayList<ComponentName>();
283         if (components != null) {
284             for (ComponentName component : components) {
285                 if (validateDream(component)) {
286                     validComponents.add(component);
287                 }
288             }
289         }
290 
291         // fallback to the default dream component if necessary
292         if (validComponents.isEmpty()) {
293             ComponentName defaultDream = getDefaultDreamComponentForUser(userId);
294             if (defaultDream != null) {
295                 Slog.w(TAG, "Falling back to default dream " + defaultDream);
296                 validComponents.add(defaultDream);
297             }
298         }
299         return validComponents.toArray(new ComponentName[validComponents.size()]);
300     }
301 
setDreamComponentsForUser(int userId, ComponentName[] componentNames)302     private void setDreamComponentsForUser(int userId, ComponentName[] componentNames) {
303         Settings.Secure.putStringForUser(mContext.getContentResolver(),
304                 Settings.Secure.SCREENSAVER_COMPONENTS,
305                 componentsToString(componentNames),
306                 userId);
307     }
308 
getDefaultDreamComponentForUser(int userId)309     private ComponentName getDefaultDreamComponentForUser(int userId) {
310         String name = Settings.Secure.getStringForUser(mContext.getContentResolver(),
311                 Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT,
312                 userId);
313         return name == null ? null : ComponentName.unflattenFromString(name);
314     }
315 
getDozeComponent()316     private ComponentName getDozeComponent() {
317         return getDozeComponent(ActivityManager.getCurrentUser());
318     }
319 
getDozeComponent(int userId)320     private ComponentName getDozeComponent(int userId) {
321         // Read the component from a system property to facilitate debugging.
322         // Note that for production devices, the dream should actually be declared in
323         // a config.xml resource.
324         String name = Build.IS_DEBUGGABLE ? SystemProperties.get("debug.doze.component") : null;
325         if (TextUtils.isEmpty(name)) {
326             // Read the component from a config.xml resource.
327             // The value should be specified in a resource overlay for the product.
328             name = mContext.getResources().getString(
329                     com.android.internal.R.string.config_dozeComponent);
330         }
331         boolean enabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
332                 Settings.Secure.DOZE_ENABLED, 1, userId) != 0;
333         return TextUtils.isEmpty(name) || !enabled ? null : ComponentName.unflattenFromString(name);
334     }
335 
getServiceInfo(ComponentName name)336     private ServiceInfo getServiceInfo(ComponentName name) {
337         try {
338             return name != null ? mContext.getPackageManager().getServiceInfo(name,
339                     PackageManager.MATCH_DEBUG_TRIAGED_MISSING) : null;
340         } catch (NameNotFoundException e) {
341             return null;
342         }
343     }
344 
startDreamLocked(final ComponentName name, final boolean isTest, final boolean canDoze, final int userId)345     private void startDreamLocked(final ComponentName name,
346             final boolean isTest, final boolean canDoze, final int userId) {
347         if (Objects.equal(mCurrentDreamName, name)
348                 && mCurrentDreamIsTest == isTest
349                 && mCurrentDreamCanDoze == canDoze
350                 && mCurrentDreamUserId == userId) {
351             return;
352         }
353 
354         stopDreamLocked(true /*immediate*/);
355 
356         Slog.i(TAG, "Entering dreamland.");
357 
358         final Binder newToken = new Binder();
359         mCurrentDreamToken = newToken;
360         mCurrentDreamName = name;
361         mCurrentDreamIsTest = isTest;
362         mCurrentDreamCanDoze = canDoze;
363         mCurrentDreamUserId = userId;
364 
365         mHandler.post(new Runnable() {
366             @Override
367             public void run() {
368                 mController.startDream(newToken, name, isTest, canDoze, userId);
369             }
370         });
371     }
372 
stopDreamLocked(final boolean immediate)373     private void stopDreamLocked(final boolean immediate) {
374         if (mCurrentDreamToken != null) {
375             if (immediate) {
376                 Slog.i(TAG, "Leaving dreamland.");
377                 cleanupDreamLocked();
378             } else if (mCurrentDreamIsWaking) {
379                 return; // already waking
380             } else {
381                 Slog.i(TAG, "Gently waking up from dream.");
382                 mCurrentDreamIsWaking = true;
383             }
384 
385             mHandler.post(new Runnable() {
386                 @Override
387                 public void run() {
388                     mController.stopDream(immediate);
389                 }
390             });
391         }
392     }
393 
cleanupDreamLocked()394     private void cleanupDreamLocked() {
395         mCurrentDreamToken = null;
396         mCurrentDreamName = null;
397         mCurrentDreamIsTest = false;
398         mCurrentDreamCanDoze = false;
399         mCurrentDreamUserId = 0;
400         mCurrentDreamIsWaking = false;
401         if (mCurrentDreamIsDozing) {
402             mCurrentDreamIsDozing = false;
403             mDozeWakeLock.release();
404         }
405         mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;
406         mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
407     }
408 
checkPermission(String permission)409     private void checkPermission(String permission) {
410         if (mContext.checkCallingOrSelfPermission(permission)
411                 != PackageManager.PERMISSION_GRANTED) {
412             throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
413                     + ", must have permission " + permission);
414         }
415     }
416 
componentsToString(ComponentName[] componentNames)417     private static String componentsToString(ComponentName[] componentNames) {
418         StringBuilder names = new StringBuilder();
419         if (componentNames != null) {
420             for (ComponentName componentName : componentNames) {
421                 if (names.length() > 0) {
422                     names.append(',');
423                 }
424                 names.append(componentName.flattenToString());
425             }
426         }
427         return names.toString();
428     }
429 
componentsFromString(String names)430     private static ComponentName[] componentsFromString(String names) {
431         if (names == null) {
432             return null;
433         }
434         String[] namesArray = names.split(",");
435         ComponentName[] componentNames = new ComponentName[namesArray.length];
436         for (int i = 0; i < namesArray.length; i++) {
437             componentNames[i] = ComponentName.unflattenFromString(namesArray[i]);
438         }
439         return componentNames;
440     }
441 
442     private final DreamController.Listener mControllerListener = new DreamController.Listener() {
443         @Override
444         public void onDreamStopped(Binder token) {
445             synchronized (mLock) {
446                 if (mCurrentDreamToken == token) {
447                     cleanupDreamLocked();
448                 }
449             }
450         }
451     };
452 
453     /**
454      * Handler for asynchronous operations performed by the dream manager.
455      * Ensures operations to {@link DreamController} are single-threaded.
456      */
457     private final class DreamHandler extends Handler {
DreamHandler(Looper looper)458         public DreamHandler(Looper looper) {
459             super(looper, null, true /*async*/);
460         }
461     }
462 
463     private final class BinderService extends IDreamManager.Stub {
464         @Override // Binder call
dump(FileDescriptor fd, PrintWriter pw, String[] args)465         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
466             if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
467                     != PackageManager.PERMISSION_GRANTED) {
468                 pw.println("Permission Denial: can't dump DreamManager from from pid="
469                         + Binder.getCallingPid()
470                         + ", uid=" + Binder.getCallingUid());
471                 return;
472             }
473 
474             final long ident = Binder.clearCallingIdentity();
475             try {
476                 dumpInternal(pw);
477             } finally {
478                 Binder.restoreCallingIdentity(ident);
479             }
480         }
481 
482         @Override // Binder call
getDreamComponents()483         public ComponentName[] getDreamComponents() {
484             checkPermission(android.Manifest.permission.READ_DREAM_STATE);
485 
486             final int userId = UserHandle.getCallingUserId();
487             final long ident = Binder.clearCallingIdentity();
488             try {
489                 return getDreamComponentsForUser(userId);
490             } finally {
491                 Binder.restoreCallingIdentity(ident);
492             }
493         }
494 
495         @Override // Binder call
setDreamComponents(ComponentName[] componentNames)496         public void setDreamComponents(ComponentName[] componentNames) {
497             checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
498 
499             final int userId = UserHandle.getCallingUserId();
500             final long ident = Binder.clearCallingIdentity();
501             try {
502                 setDreamComponentsForUser(userId, componentNames);
503             } finally {
504                 Binder.restoreCallingIdentity(ident);
505             }
506         }
507 
508         @Override // Binder call
getDefaultDreamComponent()509         public ComponentName getDefaultDreamComponent() {
510             checkPermission(android.Manifest.permission.READ_DREAM_STATE);
511 
512             final int userId = UserHandle.getCallingUserId();
513             final long ident = Binder.clearCallingIdentity();
514             try {
515                 return getDefaultDreamComponentForUser(userId);
516             } finally {
517                 Binder.restoreCallingIdentity(ident);
518             }
519         }
520 
521         @Override // Binder call
isDreaming()522         public boolean isDreaming() {
523             checkPermission(android.Manifest.permission.READ_DREAM_STATE);
524 
525             final long ident = Binder.clearCallingIdentity();
526             try {
527                 return isDreamingInternal();
528             } finally {
529                 Binder.restoreCallingIdentity(ident);
530             }
531         }
532 
533         @Override // Binder call
dream()534         public void dream() {
535             checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
536 
537             final long ident = Binder.clearCallingIdentity();
538             try {
539                 requestDreamInternal();
540             } finally {
541                 Binder.restoreCallingIdentity(ident);
542             }
543         }
544 
545         @Override // Binder call
testDream(ComponentName dream)546         public void testDream(ComponentName dream) {
547             if (dream == null) {
548                 throw new IllegalArgumentException("dream must not be null");
549             }
550             checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
551 
552             final int callingUserId = UserHandle.getCallingUserId();
553             final int currentUserId = ActivityManager.getCurrentUser();
554             if (callingUserId != currentUserId) {
555                 // This check is inherently prone to races but at least it's something.
556                 Slog.w(TAG, "Aborted attempt to start a test dream while a different "
557                         + " user is active: callingUserId=" + callingUserId
558                         + ", currentUserId=" + currentUserId);
559                 return;
560             }
561             final long ident = Binder.clearCallingIdentity();
562             try {
563                 testDreamInternal(dream, callingUserId);
564             } finally {
565                 Binder.restoreCallingIdentity(ident);
566             }
567         }
568 
569         @Override // Binder call
awaken()570         public void awaken() {
571             checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
572 
573             final long ident = Binder.clearCallingIdentity();
574             try {
575                 requestAwakenInternal();
576             } finally {
577                 Binder.restoreCallingIdentity(ident);
578             }
579         }
580 
581         @Override // Binder call
finishSelf(IBinder token, boolean immediate)582         public void finishSelf(IBinder token, boolean immediate) {
583             // Requires no permission, called by Dream from an arbitrary process.
584             if (token == null) {
585                 throw new IllegalArgumentException("token must not be null");
586             }
587 
588             final long ident = Binder.clearCallingIdentity();
589             try {
590                 finishSelfInternal(token, immediate);
591             } finally {
592                 Binder.restoreCallingIdentity(ident);
593             }
594         }
595 
596         @Override // Binder call
startDozing(IBinder token, int screenState, int screenBrightness)597         public void startDozing(IBinder token, int screenState, int screenBrightness) {
598             // Requires no permission, called by Dream from an arbitrary process.
599             if (token == null) {
600                 throw new IllegalArgumentException("token must not be null");
601             }
602 
603             final long ident = Binder.clearCallingIdentity();
604             try {
605                 startDozingInternal(token, screenState, screenBrightness);
606             } finally {
607                 Binder.restoreCallingIdentity(ident);
608             }
609         }
610 
611         @Override // Binder call
stopDozing(IBinder token)612         public void stopDozing(IBinder token) {
613             // Requires no permission, called by Dream from an arbitrary process.
614             if (token == null) {
615                 throw new IllegalArgumentException("token must not be null");
616             }
617 
618             final long ident = Binder.clearCallingIdentity();
619             try {
620                 stopDozingInternal(token);
621             } finally {
622                 Binder.restoreCallingIdentity(ident);
623             }
624         }
625     }
626 
627     private final class LocalService extends DreamManagerInternal {
628         @Override
startDream(boolean doze)629         public void startDream(boolean doze) {
630             startDreamInternal(doze);
631         }
632 
633         @Override
stopDream(boolean immediate)634         public void stopDream(boolean immediate) {
635             stopDreamInternal(immediate);
636         }
637 
638         @Override
isDreaming()639         public boolean isDreaming() {
640             return isDreamingInternal();
641         }
642     }
643 
644     private final Runnable mSystemPropertiesChanged = new Runnable() {
645         @Override
646         public void run() {
647             if (DEBUG) Slog.d(TAG, "System properties changed");
648             synchronized (mLock) {
649                 if (mCurrentDreamName != null && mCurrentDreamCanDoze
650                         && !mCurrentDreamName.equals(getDozeComponent())) {
651                     // May have updated the doze component, wake up
652                     mPowerManager.wakeUp(SystemClock.uptimeMillis(),
653                             "android.server.dreams:SYSPROP");
654                 }
655             }
656         }
657     };
658 }
659