DreamManagerService.java revision cef440f2a2bb8b6e8d082d12a67dc21f2ee65e3c
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 static android.provider.Settings.Secure.SCREENSAVER_COMPONENTS; 20import static android.provider.Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT; 21 22import android.app.ActivityManagerNative; 23import android.content.BroadcastReceiver; 24import android.content.ComponentName; 25import android.content.Context; 26import android.content.Intent; 27import android.content.IntentFilter; 28import android.content.ServiceConnection; 29import android.content.pm.PackageManager; 30import android.os.Binder; 31import android.os.Handler; 32import android.os.IBinder; 33import android.os.RemoteException; 34import android.os.UserHandle; 35import android.provider.Settings; 36import android.service.dreams.Dream; 37import android.service.dreams.IDreamManager; 38import android.util.Slog; 39 40import java.io.FileDescriptor; 41import java.io.PrintWriter; 42 43/** 44 * Service api for managing dreams. 45 * 46 * @hide 47 */ 48public final class DreamManagerService 49 extends IDreamManager.Stub 50 implements ServiceConnection { 51 private static final boolean DEBUG = true; 52 private static final String TAG = DreamManagerService.class.getSimpleName(); 53 54 private static final Intent mDreamingStartedIntent = new Intent(Dream.ACTION_DREAMING_STARTED) 55 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 56 private static final Intent mDreamingStoppedIntent = new Intent(Dream.ACTION_DREAMING_STOPPED) 57 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 58 59 private final Object mLock = new Object(); 60 private final DreamController mController; 61 private final DreamControllerHandler mHandler; 62 private final Context mContext; 63 64 private final CurrentUserManager mCurrentUserManager = new CurrentUserManager(); 65 66 private final DeathRecipient mAwakenOnBinderDeath = new DeathRecipient() { 67 @Override 68 public void binderDied() { 69 if (DEBUG) Slog.v(TAG, "binderDied()"); 70 awaken(); 71 } 72 }; 73 74 private final DreamController.Listener mControllerListener = new DreamController.Listener() { 75 @Override 76 public void onDreamStopped(boolean wasTest) { 77 synchronized(mLock) { 78 setDreamingLocked(false, wasTest); 79 } 80 }}; 81 82 private boolean mIsDreaming; 83 84 public DreamManagerService(Context context) { 85 if (DEBUG) Slog.v(TAG, "DreamManagerService startup"); 86 mContext = context; 87 mController = new DreamController(context, mAwakenOnBinderDeath, this, mControllerListener); 88 mHandler = new DreamControllerHandler(mController); 89 mController.setHandler(mHandler); 90 } 91 92 public void systemReady() { 93 mCurrentUserManager.init(mContext); 94 95 if (DEBUG) Slog.v(TAG, "Ready to dream!"); 96 } 97 98 @Override 99 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 100 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 101 102 pw.println("Dreamland:"); 103 mController.dump(pw); 104 mCurrentUserManager.dump(pw); 105 } 106 107 // begin IDreamManager api 108 @Override 109 public ComponentName[] getDreamComponents() { 110 checkPermission(android.Manifest.permission.READ_DREAM_STATE); 111 int userId = UserHandle.getCallingUserId(); 112 113 final long ident = Binder.clearCallingIdentity(); 114 try { 115 return getDreamComponentsForUser(userId); 116 } finally { 117 Binder.restoreCallingIdentity(ident); 118 } 119 } 120 121 @Override 122 public void setDreamComponents(ComponentName[] componentNames) { 123 checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); 124 int userId = UserHandle.getCallingUserId(); 125 126 final long ident = Binder.clearCallingIdentity(); 127 try { 128 Settings.Secure.putStringForUser(mContext.getContentResolver(), 129 SCREENSAVER_COMPONENTS, 130 componentsToString(componentNames), 131 userId); 132 } finally { 133 Binder.restoreCallingIdentity(ident); 134 } 135 } 136 137 @Override 138 public ComponentName getDefaultDreamComponent() { 139 checkPermission(android.Manifest.permission.READ_DREAM_STATE); 140 int userId = UserHandle.getCallingUserId(); 141 142 final long ident = Binder.clearCallingIdentity(); 143 try { 144 String name = Settings.Secure.getStringForUser(mContext.getContentResolver(), 145 SCREENSAVER_DEFAULT_COMPONENT, 146 userId); 147 return name == null ? null : ComponentName.unflattenFromString(name); 148 } finally { 149 Binder.restoreCallingIdentity(ident); 150 } 151 152 } 153 154 @Override 155 public boolean isDreaming() { 156 checkPermission(android.Manifest.permission.READ_DREAM_STATE); 157 158 return mIsDreaming; 159 } 160 161 @Override 162 public void dream() { 163 checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); 164 165 final long ident = Binder.clearCallingIdentity(); 166 try { 167 if (DEBUG) Slog.v(TAG, "Dream now"); 168 ComponentName[] dreams = getDreamComponentsForUser(mCurrentUserManager.getCurrentUserId()); 169 ComponentName firstDream = dreams != null && dreams.length > 0 ? dreams[0] : null; 170 if (firstDream != null) { 171 mHandler.requestStart(firstDream, false /*isTest*/); 172 synchronized (mLock) { 173 setDreamingLocked(true, false /*isTest*/); 174 } 175 } 176 } finally { 177 Binder.restoreCallingIdentity(ident); 178 } 179 } 180 181 @Override 182 public void testDream(ComponentName dream) { 183 checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); 184 185 final long ident = Binder.clearCallingIdentity(); 186 try { 187 if (DEBUG) Slog.v(TAG, "Test dream name=" + dream); 188 if (dream != null) { 189 mHandler.requestStart(dream, true /*isTest*/); 190 synchronized (mLock) { 191 setDreamingLocked(true, true /*isTest*/); 192 } 193 } 194 } finally { 195 Binder.restoreCallingIdentity(ident); 196 } 197 198 } 199 200 @Override 201 public void awaken() { 202 checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); 203 204 final long ident = Binder.clearCallingIdentity(); 205 try { 206 if (DEBUG) Slog.v(TAG, "Wake up"); 207 mHandler.requestStop(); 208 } finally { 209 Binder.restoreCallingIdentity(ident); 210 } 211 } 212 213 @Override 214 public void awakenSelf(IBinder token) { 215 // requires no permission, called by Dream from an arbitrary process 216 217 final long ident = Binder.clearCallingIdentity(); 218 try { 219 if (DEBUG) Slog.v(TAG, "Wake up from dream: " + token); 220 if (token != null) { 221 mHandler.requestStopSelf(token); 222 } 223 } finally { 224 Binder.restoreCallingIdentity(ident); 225 } 226 } 227 // end IDreamManager api 228 229 // begin ServiceConnection 230 @Override 231 public void onServiceConnected(ComponentName name, IBinder dream) { 232 if (DEBUG) Slog.v(TAG, "Service connected: " + name + " binder=" + 233 dream + " thread=" + Thread.currentThread().getId()); 234 mHandler.requestAttach(name, dream); 235 } 236 237 @Override 238 public void onServiceDisconnected(ComponentName name) { 239 if (DEBUG) Slog.v(TAG, "Service disconnected: " + name); 240 // Only happens in exceptional circumstances, awaken just to be safe 241 awaken(); 242 } 243 // end ServiceConnection 244 245 private void checkPermission(String permission) { 246 if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(permission)) { 247 throw new SecurityException("Access denied to process: " + Binder.getCallingPid() 248 + ", must have permission " + permission); 249 } 250 } 251 252 private void setDreamingLocked(boolean isDreaming, boolean isTest) { 253 boolean wasDreaming = mIsDreaming; 254 if (!isTest) { 255 if (!wasDreaming && isDreaming) { 256 if (DEBUG) Slog.v(TAG, "Firing ACTION_DREAMING_STARTED"); 257 mContext.sendBroadcast(mDreamingStartedIntent); 258 } else if (wasDreaming && !isDreaming) { 259 if (DEBUG) Slog.v(TAG, "Firing ACTION_DREAMING_STOPPED"); 260 mContext.sendBroadcast(mDreamingStoppedIntent); 261 } 262 } 263 mIsDreaming = isDreaming; 264 } 265 266 private ComponentName[] getDreamComponentsForUser(int userId) { 267 String names = Settings.Secure.getStringForUser(mContext.getContentResolver(), 268 SCREENSAVER_COMPONENTS, 269 userId); 270 return names == null ? null : componentsFromString(names); 271 } 272 273 private static String componentsToString(ComponentName[] componentNames) { 274 StringBuilder names = new StringBuilder(); 275 if (componentNames != null) { 276 for (ComponentName componentName : componentNames) { 277 if (names.length() > 0) { 278 names.append(','); 279 } 280 names.append(componentName.flattenToString()); 281 } 282 } 283 return names.toString(); 284 } 285 286 private static ComponentName[] componentsFromString(String names) { 287 String[] namesArray = names.split(","); 288 ComponentName[] componentNames = new ComponentName[namesArray.length]; 289 for (int i = 0; i < namesArray.length; i++) { 290 componentNames[i] = ComponentName.unflattenFromString(namesArray[i]); 291 } 292 return componentNames; 293 } 294 295 /** 296 * Keeps track of the current user, since dream() uses the current user's configuration. 297 */ 298 private static class CurrentUserManager { 299 private final Object mLock = new Object(); 300 private int mCurrentUserId; 301 302 public void init(Context context) { 303 IntentFilter filter = new IntentFilter(); 304 filter.addAction(Intent.ACTION_USER_SWITCHED); 305 context.registerReceiver(new BroadcastReceiver() { 306 @Override 307 public void onReceive(Context context, Intent intent) { 308 String action = intent.getAction(); 309 if (Intent.ACTION_USER_SWITCHED.equals(action)) { 310 synchronized(mLock) { 311 mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); 312 if (DEBUG) Slog.v(TAG, "userId " + mCurrentUserId + " is in the house"); 313 } 314 } 315 }}, filter); 316 try { 317 synchronized (mLock) { 318 mCurrentUserId = ActivityManagerNative.getDefault().getCurrentUser().id; 319 } 320 } catch (RemoteException e) { 321 Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e); 322 } 323 } 324 325 public void dump(PrintWriter pw) { 326 pw.print(" user="); pw.println(getCurrentUserId()); 327 } 328 329 public int getCurrentUserId() { 330 synchronized(mLock) { 331 return mCurrentUserId; 332 } 333 } 334 } 335 336 /** 337 * Handler for asynchronous operations performed by the dream manager. 338 * 339 * Ensures operations to {@link DreamController} are single-threaded. 340 */ 341 private static final class DreamControllerHandler extends Handler { 342 private final DreamController mController; 343 private final Runnable mStopRunnable = new Runnable() { 344 @Override 345 public void run() { 346 mController.stop(); 347 }}; 348 349 public DreamControllerHandler(DreamController controller) { 350 super(true /*async*/); 351 mController = controller; 352 } 353 354 public void requestStart(final ComponentName name, final boolean isTest) { 355 post(new Runnable(){ 356 @Override 357 public void run() { 358 mController.start(name, isTest); 359 }}); 360 } 361 362 public void requestAttach(final ComponentName name, final IBinder dream) { 363 post(new Runnable(){ 364 @Override 365 public void run() { 366 mController.attach(name, dream); 367 }}); 368 } 369 370 public void requestStopSelf(final IBinder token) { 371 post(new Runnable(){ 372 @Override 373 public void run() { 374 mController.stopSelf(token); 375 }}); 376 } 377 378 public void requestStop() { 379 post(mStopRunnable); 380 } 381 382 } 383 384} 385