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