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