Home | History | Annotate | Download | only in dreams
      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 
     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
    100     public void onStart() {
    101         publishBinderService(DreamService.DREAM_SERVICE, new BinderService());
    102         publishLocalService(DreamManagerInternal.class, new LocalService());
    103     }
    104 
    105     @Override
    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 
    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) {
    141                 mController.dump(pw);
    142             }
    143         }, pw, 200);
    144     }
    145 
    146     private boolean isDreamingInternal() {
    147         synchronized (mLock) {
    148             return mCurrentDreamToken != null && !mCurrentDreamIsTest
    149                     && !mCurrentDreamIsWaking;
    150         }
    151     }
    152 
    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 
    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 
    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 
    191     private void testDreamInternal(ComponentName dream, int userId) {
    192         synchronized (mLock) {
    193             startDreamLocked(dream, true /*isTest*/, false /*canDoze*/, userId);
    194         }
    195     }
    196 
    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 
    207     private void stopDreamInternal(boolean immediate) {
    208         synchronized (mLock) {
    209             stopDreamLocked(immediate);
    210         }
    211     }
    212 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    316     private ComponentName getDozeComponent() {
    317         return getDozeComponent(ActivityManager.getCurrentUser());
    318     }
    319 
    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 
    336     private ServiceInfo getServiceInfo(ComponentName name) {
    337         try {
    338             return name != null ? mContext.getPackageManager().getServiceInfo(name, 0) : null;
    339         } catch (NameNotFoundException e) {
    340             return null;
    341         }
    342     }
    343 
    344     private void startDreamLocked(final ComponentName name,
    345             final boolean isTest, final boolean canDoze, final int userId) {
    346         if (Objects.equal(mCurrentDreamName, name)
    347                 && mCurrentDreamIsTest == isTest
    348                 && mCurrentDreamCanDoze == canDoze
    349                 && mCurrentDreamUserId == userId) {
    350             return;
    351         }
    352 
    353         stopDreamLocked(true /*immediate*/);
    354 
    355         Slog.i(TAG, "Entering dreamland.");
    356 
    357         final Binder newToken = new Binder();
    358         mCurrentDreamToken = newToken;
    359         mCurrentDreamName = name;
    360         mCurrentDreamIsTest = isTest;
    361         mCurrentDreamCanDoze = canDoze;
    362         mCurrentDreamUserId = userId;
    363 
    364         mHandler.post(new Runnable() {
    365             @Override
    366             public void run() {
    367                 mController.startDream(newToken, name, isTest, canDoze, userId);
    368             }
    369         });
    370     }
    371 
    372     private void stopDreamLocked(final boolean immediate) {
    373         if (mCurrentDreamToken != null) {
    374             if (immediate) {
    375                 Slog.i(TAG, "Leaving dreamland.");
    376                 cleanupDreamLocked();
    377             } else if (mCurrentDreamIsWaking) {
    378                 return; // already waking
    379             } else {
    380                 Slog.i(TAG, "Gently waking up from dream.");
    381                 mCurrentDreamIsWaking = true;
    382             }
    383 
    384             mHandler.post(new Runnable() {
    385                 @Override
    386                 public void run() {
    387                     mController.stopDream(immediate);
    388                 }
    389             });
    390         }
    391     }
    392 
    393     private void cleanupDreamLocked() {
    394         mCurrentDreamToken = null;
    395         mCurrentDreamName = null;
    396         mCurrentDreamIsTest = false;
    397         mCurrentDreamCanDoze = false;
    398         mCurrentDreamUserId = 0;
    399         mCurrentDreamIsWaking = false;
    400         if (mCurrentDreamIsDozing) {
    401             mCurrentDreamIsDozing = false;
    402             mDozeWakeLock.release();
    403         }
    404         mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;
    405         mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
    406     }
    407 
    408     private void checkPermission(String permission) {
    409         if (mContext.checkCallingOrSelfPermission(permission)
    410                 != PackageManager.PERMISSION_GRANTED) {
    411             throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
    412                     + ", must have permission " + permission);
    413         }
    414     }
    415 
    416     private static String componentsToString(ComponentName[] componentNames) {
    417         StringBuilder names = new StringBuilder();
    418         if (componentNames != null) {
    419             for (ComponentName componentName : componentNames) {
    420                 if (names.length() > 0) {
    421                     names.append(',');
    422                 }
    423                 names.append(componentName.flattenToString());
    424             }
    425         }
    426         return names.toString();
    427     }
    428 
    429     private static ComponentName[] componentsFromString(String names) {
    430         if (names == null) {
    431             return null;
    432         }
    433         String[] namesArray = names.split(",");
    434         ComponentName[] componentNames = new ComponentName[namesArray.length];
    435         for (int i = 0; i < namesArray.length; i++) {
    436             componentNames[i] = ComponentName.unflattenFromString(namesArray[i]);
    437         }
    438         return componentNames;
    439     }
    440 
    441     private final DreamController.Listener mControllerListener = new DreamController.Listener() {
    442         @Override
    443         public void onDreamStopped(Binder token) {
    444             synchronized (mLock) {
    445                 if (mCurrentDreamToken == token) {
    446                     cleanupDreamLocked();
    447                 }
    448             }
    449         }
    450     };
    451 
    452     /**
    453      * Handler for asynchronous operations performed by the dream manager.
    454      * Ensures operations to {@link DreamController} are single-threaded.
    455      */
    456     private final class DreamHandler extends Handler {
    457         public DreamHandler(Looper looper) {
    458             super(looper, null, true /*async*/);
    459         }
    460     }
    461 
    462     private final class BinderService extends IDreamManager.Stub {
    463         @Override // Binder call
    464         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    465             if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
    466                     != PackageManager.PERMISSION_GRANTED) {
    467                 pw.println("Permission Denial: can't dump DreamManager from from pid="
    468                         + Binder.getCallingPid()
    469                         + ", uid=" + Binder.getCallingUid());
    470                 return;
    471             }
    472 
    473             final long ident = Binder.clearCallingIdentity();
    474             try {
    475                 dumpInternal(pw);
    476             } finally {
    477                 Binder.restoreCallingIdentity(ident);
    478             }
    479         }
    480 
    481         @Override // Binder call
    482         public ComponentName[] getDreamComponents() {
    483             checkPermission(android.Manifest.permission.READ_DREAM_STATE);
    484 
    485             final int userId = UserHandle.getCallingUserId();
    486             final long ident = Binder.clearCallingIdentity();
    487             try {
    488                 return getDreamComponentsForUser(userId);
    489             } finally {
    490                 Binder.restoreCallingIdentity(ident);
    491             }
    492         }
    493 
    494         @Override // Binder call
    495         public void setDreamComponents(ComponentName[] componentNames) {
    496             checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
    497 
    498             final int userId = UserHandle.getCallingUserId();
    499             final long ident = Binder.clearCallingIdentity();
    500             try {
    501                 setDreamComponentsForUser(userId, componentNames);
    502             } finally {
    503                 Binder.restoreCallingIdentity(ident);
    504             }
    505         }
    506 
    507         @Override // Binder call
    508         public ComponentName getDefaultDreamComponent() {
    509             checkPermission(android.Manifest.permission.READ_DREAM_STATE);
    510 
    511             final int userId = UserHandle.getCallingUserId();
    512             final long ident = Binder.clearCallingIdentity();
    513             try {
    514                 return getDefaultDreamComponentForUser(userId);
    515             } finally {
    516                 Binder.restoreCallingIdentity(ident);
    517             }
    518         }
    519 
    520         @Override // Binder call
    521         public boolean isDreaming() {
    522             checkPermission(android.Manifest.permission.READ_DREAM_STATE);
    523 
    524             final long ident = Binder.clearCallingIdentity();
    525             try {
    526                 return isDreamingInternal();
    527             } finally {
    528                 Binder.restoreCallingIdentity(ident);
    529             }
    530         }
    531 
    532         @Override // Binder call
    533         public void dream() {
    534             checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
    535 
    536             final long ident = Binder.clearCallingIdentity();
    537             try {
    538                 requestDreamInternal();
    539             } finally {
    540                 Binder.restoreCallingIdentity(ident);
    541             }
    542         }
    543 
    544         @Override // Binder call
    545         public void testDream(ComponentName dream) {
    546             if (dream == null) {
    547                 throw new IllegalArgumentException("dream must not be null");
    548             }
    549             checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
    550 
    551             final int callingUserId = UserHandle.getCallingUserId();
    552             final int currentUserId = ActivityManager.getCurrentUser();
    553             if (callingUserId != currentUserId) {
    554                 // This check is inherently prone to races but at least it's something.
    555                 Slog.w(TAG, "Aborted attempt to start a test dream while a different "
    556                         + " user is active: callingUserId=" + callingUserId
    557                         + ", currentUserId=" + currentUserId);
    558                 return;
    559             }
    560             final long ident = Binder.clearCallingIdentity();
    561             try {
    562                 testDreamInternal(dream, callingUserId);
    563             } finally {
    564                 Binder.restoreCallingIdentity(ident);
    565             }
    566         }
    567 
    568         @Override // Binder call
    569         public void awaken() {
    570             checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
    571 
    572             final long ident = Binder.clearCallingIdentity();
    573             try {
    574                 requestAwakenInternal();
    575             } finally {
    576                 Binder.restoreCallingIdentity(ident);
    577             }
    578         }
    579 
    580         @Override // Binder call
    581         public void finishSelf(IBinder token, boolean immediate) {
    582             // Requires no permission, called by Dream from an arbitrary process.
    583             if (token == null) {
    584                 throw new IllegalArgumentException("token must not be null");
    585             }
    586 
    587             final long ident = Binder.clearCallingIdentity();
    588             try {
    589                 finishSelfInternal(token, immediate);
    590             } finally {
    591                 Binder.restoreCallingIdentity(ident);
    592             }
    593         }
    594 
    595         @Override // Binder call
    596         public void startDozing(IBinder token, int screenState, int screenBrightness) {
    597             // Requires no permission, called by Dream from an arbitrary process.
    598             if (token == null) {
    599                 throw new IllegalArgumentException("token must not be null");
    600             }
    601 
    602             final long ident = Binder.clearCallingIdentity();
    603             try {
    604                 startDozingInternal(token, screenState, screenBrightness);
    605             } finally {
    606                 Binder.restoreCallingIdentity(ident);
    607             }
    608         }
    609 
    610         @Override // Binder call
    611         public void stopDozing(IBinder token) {
    612             // Requires no permission, called by Dream from an arbitrary process.
    613             if (token == null) {
    614                 throw new IllegalArgumentException("token must not be null");
    615             }
    616 
    617             final long ident = Binder.clearCallingIdentity();
    618             try {
    619                 stopDozingInternal(token);
    620             } finally {
    621                 Binder.restoreCallingIdentity(ident);
    622             }
    623         }
    624     }
    625 
    626     private final class LocalService extends DreamManagerInternal {
    627         @Override
    628         public void startDream(boolean doze) {
    629             startDreamInternal(doze);
    630         }
    631 
    632         @Override
    633         public void stopDream(boolean immediate) {
    634             stopDreamInternal(immediate);
    635         }
    636 
    637         @Override
    638         public boolean isDreaming() {
    639             return isDreamingInternal();
    640         }
    641     }
    642 
    643     private final Runnable mSystemPropertiesChanged = new Runnable() {
    644         @Override
    645         public void run() {
    646             if (DEBUG) Slog.d(TAG, "System properties changed");
    647             synchronized (mLock) {
    648                 if (mCurrentDreamName != null && mCurrentDreamCanDoze
    649                         && !mCurrentDreamName.equals(getDozeComponent())) {
    650                     // May have updated the doze component, wake up
    651                     mPowerManager.wakeUp(SystemClock.uptimeMillis());
    652                 }
    653             }
    654         }
    655     };
    656 }
    657