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