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 com.android.internal.util.DumpUtils;
     20 
     21 import android.app.ActivityManager;
     22 import android.content.BroadcastReceiver;
     23 import android.content.ComponentName;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.content.IntentFilter;
     27 import android.content.pm.PackageManager;
     28 import android.os.Binder;
     29 import android.os.Handler;
     30 import android.os.IBinder;
     31 import android.os.Looper;
     32 import android.os.PowerManager;
     33 import android.os.SystemClock;
     34 import android.os.UserHandle;
     35 import android.provider.Settings;
     36 import android.service.dreams.IDreamManager;
     37 import android.util.Slog;
     38 
     39 import java.io.FileDescriptor;
     40 import java.io.PrintWriter;
     41 
     42 import libcore.util.Objects;
     43 
     44 /**
     45  * Service api for managing dreams.
     46  *
     47  * @hide
     48  */
     49 public final class DreamManagerService extends IDreamManager.Stub {
     50     private static final boolean DEBUG = true;
     51     private static final String TAG = "DreamManagerService";
     52 
     53     private final Object mLock = new Object();
     54 
     55     private final Context mContext;
     56     private final DreamHandler mHandler;
     57     private final DreamController mController;
     58     private final PowerManager mPowerManager;
     59 
     60     private Binder mCurrentDreamToken;
     61     private ComponentName mCurrentDreamName;
     62     private int mCurrentDreamUserId;
     63     private boolean mCurrentDreamIsTest;
     64 
     65     public DreamManagerService(Context context, Handler mainHandler) {
     66         mContext = context;
     67         mHandler = new DreamHandler(mainHandler.getLooper());
     68         mController = new DreamController(context, mHandler, mControllerListener);
     69 
     70         mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
     71     }
     72 
     73     public void systemReady() {
     74         mContext.registerReceiver(new BroadcastReceiver() {
     75             @Override
     76             public void onReceive(Context context, Intent intent) {
     77                 synchronized (mLock) {
     78                     stopDreamLocked();
     79                 }
     80             }
     81         }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
     82     }
     83 
     84     @Override
     85     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
     86         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
     87 
     88         pw.println("DREAM MANAGER (dumpsys dreams)");
     89         pw.println();
     90 
     91         pw.println("mCurrentDreamToken=" + mCurrentDreamToken);
     92         pw.println("mCurrentDreamName=" + mCurrentDreamName);
     93         pw.println("mCurrentDreamUserId=" + mCurrentDreamUserId);
     94         pw.println("mCurrentDreamIsTest=" + mCurrentDreamIsTest);
     95         pw.println();
     96 
     97         DumpUtils.dumpAsync(mHandler, new DumpUtils.Dump() {
     98             @Override
     99             public void dump(PrintWriter pw) {
    100                 mController.dump(pw);
    101             }
    102         }, pw, 200);
    103     }
    104 
    105     @Override // Binder call
    106     public ComponentName[] getDreamComponents() {
    107         checkPermission(android.Manifest.permission.READ_DREAM_STATE);
    108 
    109         final int userId = UserHandle.getCallingUserId();
    110         final long ident = Binder.clearCallingIdentity();
    111         try {
    112             return getDreamComponentsForUser(userId);
    113         } finally {
    114             Binder.restoreCallingIdentity(ident);
    115         }
    116     }
    117 
    118     @Override // Binder call
    119     public void setDreamComponents(ComponentName[] componentNames) {
    120         checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
    121 
    122         final int userId = UserHandle.getCallingUserId();
    123         final long ident = Binder.clearCallingIdentity();
    124         try {
    125             Settings.Secure.putStringForUser(mContext.getContentResolver(),
    126                     Settings.Secure.SCREENSAVER_COMPONENTS,
    127                     componentsToString(componentNames),
    128                     userId);
    129         } finally {
    130             Binder.restoreCallingIdentity(ident);
    131         }
    132     }
    133 
    134     @Override // Binder call
    135     public ComponentName getDefaultDreamComponent() {
    136         checkPermission(android.Manifest.permission.READ_DREAM_STATE);
    137 
    138         final int userId = UserHandle.getCallingUserId();
    139         final long ident = Binder.clearCallingIdentity();
    140         try {
    141             String name = Settings.Secure.getStringForUser(mContext.getContentResolver(),
    142                     Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT,
    143                     userId);
    144             return name == null ? null : ComponentName.unflattenFromString(name);
    145         } finally {
    146             Binder.restoreCallingIdentity(ident);
    147         }
    148     }
    149 
    150     @Override // Binder call
    151     public boolean isDreaming() {
    152         checkPermission(android.Manifest.permission.READ_DREAM_STATE);
    153 
    154         synchronized (mLock) {
    155             return mCurrentDreamToken != null && !mCurrentDreamIsTest;
    156         }
    157     }
    158 
    159     @Override // Binder call
    160     public void dream() {
    161         checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
    162 
    163         final long ident = Binder.clearCallingIdentity();
    164         try {
    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         } finally {
    173             Binder.restoreCallingIdentity(ident);
    174         }
    175     }
    176 
    177     @Override // Binder call
    178     public void testDream(ComponentName dream) {
    179         checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
    180 
    181         if (dream == null) {
    182             throw new IllegalArgumentException("dream must not be null");
    183         }
    184 
    185         final int callingUserId = UserHandle.getCallingUserId();
    186         final int currentUserId = ActivityManager.getCurrentUser();
    187         if (callingUserId != currentUserId) {
    188             // This check is inherently prone to races but at least it's something.
    189             Slog.w(TAG, "Aborted attempt to start a test dream while a different "
    190                     + " user is active: callingUserId=" + callingUserId
    191                     + ", currentUserId=" + currentUserId);
    192             return;
    193         }
    194         final long ident = Binder.clearCallingIdentity();
    195         try {
    196             synchronized (mLock) {
    197                 startDreamLocked(dream, true /*isTest*/, callingUserId);
    198             }
    199         } finally {
    200             Binder.restoreCallingIdentity(ident);
    201         }
    202     }
    203 
    204     @Override // Binder call
    205     public void awaken() {
    206         checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
    207 
    208         final long ident = Binder.clearCallingIdentity();
    209         try {
    210             // Treat an explicit request to awaken as user activity so that the
    211             // device doesn't immediately go to sleep if the timeout expired,
    212             // for example when being undocked.
    213             long time = SystemClock.uptimeMillis();
    214             mPowerManager.userActivity(time, false /*noChangeLights*/);
    215             stopDream();
    216         } finally {
    217             Binder.restoreCallingIdentity(ident);
    218         }
    219     }
    220 
    221     @Override // Binder call
    222     public void finishSelf(IBinder token) {
    223         // Requires no permission, called by Dream from an arbitrary process.
    224         if (token == null) {
    225             throw new IllegalArgumentException("token must not be null");
    226         }
    227 
    228         final long ident = Binder.clearCallingIdentity();
    229         try {
    230             if (DEBUG) {
    231                 Slog.d(TAG, "Dream finished: " + token);
    232             }
    233 
    234             // Note that a dream finishing and self-terminating is not
    235             // itself considered user activity.  If the dream is ending because
    236             // the user interacted with the device then user activity will already
    237             // have been poked so the device will stay awake a bit longer.
    238             // If the dream is ending on its own for other reasons and no wake
    239             // locks are held and the user activity timeout has expired then the
    240             // device may simply go to sleep.
    241             synchronized (mLock) {
    242                 if (mCurrentDreamToken == token) {
    243                     stopDreamLocked();
    244                 }
    245             }
    246         } finally {
    247             Binder.restoreCallingIdentity(ident);
    248         }
    249     }
    250 
    251     /**
    252      * Called by the power manager to start a dream.
    253      */
    254     public void startDream() {
    255         int userId = ActivityManager.getCurrentUser();
    256         ComponentName dream = chooseDreamForUser(userId);
    257         if (dream != null) {
    258             synchronized (mLock) {
    259                 startDreamLocked(dream, false /*isTest*/, userId);
    260             }
    261         }
    262     }
    263 
    264     /**
    265      * Called by the power manager to stop a dream.
    266      */
    267     public void stopDream() {
    268         synchronized (mLock) {
    269             stopDreamLocked();
    270         }
    271     }
    272 
    273     private ComponentName chooseDreamForUser(int userId) {
    274         ComponentName[] dreams = getDreamComponentsForUser(userId);
    275         return dreams != null && dreams.length != 0 ? dreams[0] : null;
    276     }
    277 
    278     private ComponentName[] getDreamComponentsForUser(int userId) {
    279         String names = Settings.Secure.getStringForUser(mContext.getContentResolver(),
    280                 Settings.Secure.SCREENSAVER_COMPONENTS,
    281                 userId);
    282         return names == null ? null : componentsFromString(names);
    283     }
    284 
    285     private void startDreamLocked(final ComponentName name,
    286             final boolean isTest, final int userId) {
    287         if (Objects.equal(mCurrentDreamName, name)
    288                 && mCurrentDreamIsTest == isTest
    289                 && mCurrentDreamUserId == userId) {
    290             return;
    291         }
    292 
    293         stopDreamLocked();
    294 
    295         Slog.i(TAG, "Entering dreamland.");
    296 
    297         final Binder newToken = new Binder();
    298         mCurrentDreamToken = newToken;
    299         mCurrentDreamName = name;
    300         mCurrentDreamIsTest = isTest;
    301         mCurrentDreamUserId = userId;
    302 
    303         mHandler.post(new Runnable() {
    304             @Override
    305             public void run() {
    306                 mController.startDream(newToken, name, isTest, userId);
    307             }
    308         });
    309     }
    310 
    311     private void stopDreamLocked() {
    312         if (mCurrentDreamToken != null) {
    313             Slog.i(TAG, "Leaving dreamland.");
    314 
    315             cleanupDreamLocked();
    316 
    317             mHandler.post(new Runnable() {
    318                 @Override
    319                 public void run() {
    320                     mController.stopDream();
    321                 }
    322             });
    323         }
    324     }
    325 
    326     private void cleanupDreamLocked() {
    327         mCurrentDreamToken = null;
    328         mCurrentDreamName = null;
    329         mCurrentDreamIsTest = false;
    330         mCurrentDreamUserId = 0;
    331     }
    332 
    333     private void checkPermission(String permission) {
    334         if (mContext.checkCallingOrSelfPermission(permission)
    335                 != PackageManager.PERMISSION_GRANTED) {
    336             throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
    337                     + ", must have permission " + permission);
    338         }
    339     }
    340 
    341     private static String componentsToString(ComponentName[] componentNames) {
    342         StringBuilder names = new StringBuilder();
    343         if (componentNames != null) {
    344             for (ComponentName componentName : componentNames) {
    345                 if (names.length() > 0) {
    346                     names.append(',');
    347                 }
    348                 names.append(componentName.flattenToString());
    349             }
    350         }
    351         return names.toString();
    352     }
    353 
    354     private static ComponentName[] componentsFromString(String names) {
    355         String[] namesArray = names.split(",");
    356         ComponentName[] componentNames = new ComponentName[namesArray.length];
    357         for (int i = 0; i < namesArray.length; i++) {
    358             componentNames[i] = ComponentName.unflattenFromString(namesArray[i]);
    359         }
    360         return componentNames;
    361     }
    362 
    363     private final DreamController.Listener mControllerListener = new DreamController.Listener() {
    364         @Override
    365         public void onDreamStopped(Binder token) {
    366             synchronized (mLock) {
    367                 if (mCurrentDreamToken == token) {
    368                     cleanupDreamLocked();
    369                 }
    370             }
    371         }
    372     };
    373 
    374     /**
    375      * Handler for asynchronous operations performed by the dream manager.
    376      * Ensures operations to {@link DreamController} are single-threaded.
    377      */
    378     private final class DreamHandler extends Handler {
    379         public DreamHandler(Looper looper) {
    380             super(looper, null, true /*async*/);
    381         }
    382     }
    383 }
    384