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