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