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
17package com.android.server.dreams;
18
19import com.android.internal.util.DumpUtils;
20
21import android.app.ActivityManager;
22import android.content.BroadcastReceiver;
23import android.content.ComponentName;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
27import android.content.pm.PackageManager;
28import android.os.Binder;
29import android.os.Handler;
30import android.os.IBinder;
31import android.os.Looper;
32import android.os.PowerManager;
33import android.os.SystemClock;
34import android.os.UserHandle;
35import android.provider.Settings;
36import android.service.dreams.IDreamManager;
37import android.util.Slog;
38
39import java.io.FileDescriptor;
40import java.io.PrintWriter;
41
42import libcore.util.Objects;
43
44/**
45 * Service api for managing dreams.
46 *
47 * @hide
48 */
49public 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