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