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