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