WallpaperService.java revision 3be63c09309b21c01b535271625d4c39045690e5
1/* 2 * Copyright (C) 2009 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 android.service.wallpaper; 18 19import com.android.internal.os.HandlerCaller; 20import com.android.internal.view.BaseIWindow; 21import com.android.internal.view.BaseSurfaceHolder; 22 23import android.app.Service; 24import android.app.WallpaperManager; 25import android.content.Intent; 26import android.graphics.Rect; 27import android.os.IBinder; 28import android.os.Message; 29import android.os.RemoteException; 30import android.util.Log; 31import android.view.Gravity; 32import android.view.IWindowSession; 33import android.view.MotionEvent; 34import android.view.SurfaceHolder; 35import android.view.View; 36import android.view.ViewGroup; 37import android.view.ViewRoot; 38import android.view.WindowManager; 39import android.view.WindowManagerImpl; 40 41/** 42 * A wallpaper service is responsible for showing a live wallpaper behind 43 * applications that would like to sit on top of it. 44 * @hide Live Wallpaper 45 */ 46public abstract class WallpaperService extends Service { 47 /** 48 * The {@link Intent} that must be declared as handled by the service. 49 */ 50 public static final String SERVICE_INTERFACE = 51 "android.service.wallpaper.WallpaperService"; 52 53 static final String TAG = "WallpaperService"; 54 static final boolean DEBUG = false; 55 56 private static final int DO_ATTACH = 10; 57 private static final int DO_DETACH = 20; 58 59 private static final int MSG_UPDATE_SURFACE = 10000; 60 private static final int MSG_VISIBILITY_CHANGED = 10010; 61 private static final int MSG_WALLPAPER_OFFSETS = 10020; 62 private static final int MSG_WINDOW_RESIZED = 10030; 63 private static final int MSG_TOUCH_EVENT = 10040; 64 65 /** 66 * The actual implementation of a wallpaper. A wallpaper service may 67 * have multiple instances running (for example as a real wallpaper 68 * and as a preview), each of which is represented by its own Engine 69 * instance. You must implement {@link WallpaperService#onCreateEngine()} 70 * to return your concrete Engine implementation. 71 */ 72 public class Engine { 73 IWallpaperEngineWrapper mIWallpaperEngine; 74 75 // Copies from mIWallpaperEngine. 76 HandlerCaller mCaller; 77 IWallpaperConnection mConnection; 78 IBinder mWindowToken; 79 80 boolean mInitializing = true; 81 82 // Current window state. 83 boolean mCreated; 84 boolean mIsCreating; 85 boolean mDrawingAllowed; 86 int mWidth; 87 int mHeight; 88 int mFormat; 89 int mType; 90 int mCurWidth; 91 int mCurHeight; 92 int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; 93 int mCurWindowFlags = mWindowFlags; 94 boolean mDestroyReportNeeded; 95 final Rect mVisibleInsets = new Rect(); 96 final Rect mWinFrame = new Rect(); 97 final Rect mContentInsets = new Rect(); 98 99 final WindowManager.LayoutParams mLayout 100 = new WindowManager.LayoutParams(); 101 IWindowSession mSession; 102 103 final Object mLock = new Object(); 104 boolean mOffsetMessageEnqueued; 105 float mPendingXOffset; 106 float mPendingYOffset; 107 MotionEvent mPendingMove; 108 109 final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() { 110 111 @Override 112 public boolean onAllowLockCanvas() { 113 return mDrawingAllowed; 114 } 115 116 @Override 117 public void onRelayoutContainer() { 118 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE); 119 mCaller.sendMessage(msg); 120 } 121 122 @Override 123 public void onUpdateSurface() { 124 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE); 125 mCaller.sendMessage(msg); 126 } 127 128 public boolean isCreating() { 129 return mIsCreating; 130 } 131 132 public void setKeepScreenOn(boolean screenOn) { 133 // Ignore. 134 } 135 136 }; 137 138 final BaseIWindow mWindow = new BaseIWindow() { 139 @Override 140 public boolean onDispatchPointer(MotionEvent event, long eventTime, 141 boolean callWhenDone) { 142 synchronized (mLock) { 143 if (event.getAction() == MotionEvent.ACTION_MOVE) { 144 if (mPendingMove != null) { 145 mCaller.removeMessages(MSG_TOUCH_EVENT, mPendingMove); 146 mPendingMove.recycle(); 147 } 148 mPendingMove = event; 149 } else { 150 mPendingMove = null; 151 } 152 Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, 153 event); 154 mCaller.sendMessage(msg); 155 } 156 return false; 157 } 158 159 @Override 160 public void resized(int w, int h, Rect coveredInsets, 161 Rect visibleInsets, boolean reportDraw) { 162 Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED, 163 reportDraw ? 1 : 0); 164 mCaller.sendMessage(msg); 165 } 166 167 @Override 168 public void dispatchAppVisibility(boolean visible) { 169 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 170 visible ? 1 : 0); 171 mCaller.sendMessage(msg); 172 } 173 174 @Override 175 public void dispatchWallpaperOffsets(float x, float y) { 176 synchronized (mLock) { 177 mPendingXOffset = x; 178 mPendingYOffset = y; 179 if (!mOffsetMessageEnqueued) { 180 mOffsetMessageEnqueued = true; 181 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS); 182 mCaller.sendMessage(msg); 183 } 184 } 185 } 186 187 }; 188 189 /** 190 * Provides access to the surface in which this wallpaper is drawn. 191 */ 192 public SurfaceHolder getSurfaceHolder() { 193 return mSurfaceHolder; 194 } 195 196 /** 197 * Convenience for {@link WallpaperManager#getDesiredMinimumWidth() 198 * WallpaperManager.getDesiredMinimumWidth()}, returning the width 199 * that the system would like this wallpaper to run in. 200 */ 201 public int getDesiredMinimumWidth() { 202 return mIWallpaperEngine.mReqWidth; 203 } 204 205 /** 206 * Convenience for {@link WallpaperManager#getDesiredMinimumHeight() 207 * WallpaperManager.getDesiredMinimumHeight()}, returning the height 208 * that the system would like this wallpaper to run in. 209 */ 210 public int getDesiredMinimumHeight() { 211 return mIWallpaperEngine.mReqHeight; 212 } 213 214 /** 215 * Returns true if this engine is running in preview mode -- that is, 216 * it is being shown to the user before they select it as the actual 217 * wallpaper. 218 */ 219 public boolean isPreview() { 220 return mIWallpaperEngine.mIsPreview; 221 } 222 223 /** 224 * Control whether this wallpaper will receive raw touch events 225 * from the window manager as the user interacts with the window 226 * that is currently displaying the wallpaper. By default they 227 * are turned off. If enabled, the events will be received in 228 * {@link #onTouchEvent(MotionEvent)}. 229 */ 230 public void setTouchEventsEnabled(boolean enabled) { 231 mWindowFlags = enabled 232 ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) 233 : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); 234 if (mCreated) { 235 updateSurface(false, false); 236 } 237 } 238 239 /** 240 * Called once to initialize the engine. After returning, the 241 * engine's surface will be created by the framework. 242 */ 243 public void onCreate(SurfaceHolder surfaceHolder) { 244 } 245 246 /** 247 * Called right before the engine is going away. After this the 248 * surface will be destroyed and this Engine object is no longer 249 * valid. 250 */ 251 public void onDestroy() { 252 } 253 254 /** 255 * Called to inform you of the wallpaper becoming visible or 256 * hidden. <em>It is very important that a wallpaper only use 257 * CPU while it is visible.</em>. 258 */ 259 public void onVisibilityChanged(boolean visible) { 260 } 261 262 /** 263 * Called as the user performs touch-screen interaction with the 264 * window that is currently showing this wallpaper. Note that the 265 * events you receive here are driven by the actual application the 266 * user is interacting with, so if it is slow you will get viewer 267 * move events. 268 */ 269 public void onTouchEvent(MotionEvent event) { 270 } 271 272 /** 273 * Called to inform you of the wallpaper's offsets changing 274 * within its contain, corresponding to the container's 275 * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float) 276 * WallpaperManager.setWallpaperOffsets()}. 277 */ 278 public void onOffsetsChanged(float xOffset, float yOffset, 279 int xPixelOffset, int yPixelOffset) { 280 } 281 282 /** 283 * Convenience for {@link SurfaceHolder.Callback#surfaceChanged 284 * SurfaceHolder.Callback.surfaceChanged()}. 285 */ 286 public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { 287 } 288 289 /** 290 * Convenience for {@link SurfaceHolder.Callback#surfaceCreated 291 * SurfaceHolder.Callback.surfaceCreated()}. 292 */ 293 public void onSurfaceCreated(SurfaceHolder holder) { 294 } 295 296 /** 297 * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed 298 * SurfaceHolder.Callback.surfaceDestroyed()}. 299 */ 300 public void onSurfaceDestroyed(SurfaceHolder holder) { 301 } 302 303 void updateSurface(boolean forceRelayout, boolean forceReport) { 304 int myWidth = mSurfaceHolder.getRequestedWidth(); 305 if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.FILL_PARENT; 306 int myHeight = mSurfaceHolder.getRequestedHeight(); 307 if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.FILL_PARENT; 308 309 final boolean creating = !mCreated; 310 final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat(); 311 boolean sizeChanged = mWidth != myWidth || mHeight != myHeight; 312 final boolean typeChanged = mType != mSurfaceHolder.getRequestedType(); 313 final boolean flagsChanged = mCurWindowFlags != mWindowFlags; 314 if (forceRelayout || creating || formatChanged || sizeChanged 315 || typeChanged || flagsChanged) { 316 317 if (DEBUG) Log.i(TAG, "Changes: creating=" + creating 318 + " format=" + formatChanged + " size=" + sizeChanged); 319 320 try { 321 mWidth = myWidth; 322 mHeight = myHeight; 323 mFormat = mSurfaceHolder.getRequestedFormat(); 324 mType = mSurfaceHolder.getRequestedType(); 325 326 mLayout.x = 0; 327 mLayout.y = 0; 328 mLayout.width = myWidth; 329 mLayout.height = myHeight; 330 331 mLayout.format = mFormat; 332 333 mCurWindowFlags = mWindowFlags; 334 mLayout.flags = mWindowFlags 335 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 336 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 337 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 338 ; 339 340 mLayout.memoryType = mType; 341 mLayout.token = mWindowToken; 342 343 if (!mCreated) { 344 mLayout.type = mIWallpaperEngine.mWindowType; 345 mLayout.gravity = Gravity.LEFT|Gravity.TOP; 346 mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets); 347 } 348 349 mSurfaceHolder.mSurfaceLock.lock(); 350 mDrawingAllowed = true; 351 352 final int relayoutResult = mSession.relayout( 353 mWindow, mLayout, mWidth, mHeight, 354 View.VISIBLE, false, mWinFrame, mContentInsets, 355 mVisibleInsets, mSurfaceHolder.mSurface); 356 357 if (DEBUG) Log.i(TAG, "New surface: " + mSurfaceHolder.mSurface 358 + ", frame=" + mWinFrame); 359 360 int w = mWinFrame.width(); 361 if (mCurWidth != w) { 362 sizeChanged = true; 363 mCurWidth = w; 364 } 365 int h = mWinFrame.height(); 366 if (mCurHeight != h) { 367 sizeChanged = true; 368 mCurHeight = h; 369 } 370 371 mSurfaceHolder.mSurfaceLock.unlock(); 372 373 try { 374 mDestroyReportNeeded = true; 375 376 SurfaceHolder.Callback callbacks[] = null; 377 synchronized (mSurfaceHolder.mCallbacks) { 378 final int N = mSurfaceHolder.mCallbacks.size(); 379 if (N > 0) { 380 callbacks = new SurfaceHolder.Callback[N]; 381 mSurfaceHolder.mCallbacks.toArray(callbacks); 382 } 383 } 384 385 if (!mCreated) { 386 mIsCreating = true; 387 onSurfaceCreated(mSurfaceHolder); 388 if (callbacks != null) { 389 for (SurfaceHolder.Callback c : callbacks) { 390 c.surfaceCreated(mSurfaceHolder); 391 } 392 } 393 } 394 if (forceReport || creating || formatChanged || sizeChanged) { 395 if (DEBUG) { 396 RuntimeException e = new RuntimeException(); 397 e.fillInStackTrace(); 398 Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating 399 + " formatChanged=" + formatChanged 400 + " sizeChanged=" + sizeChanged, e); 401 } 402 onSurfaceChanged(mSurfaceHolder, mFormat, 403 mCurWidth, mCurHeight); 404 if (callbacks != null) { 405 for (SurfaceHolder.Callback c : callbacks) { 406 c.surfaceChanged(mSurfaceHolder, mFormat, 407 mCurWidth, mCurHeight); 408 } 409 } 410 } 411 } finally { 412 mIsCreating = false; 413 mCreated = true; 414 if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) { 415 mSession.finishDrawing(mWindow); 416 } 417 } 418 } catch (RemoteException ex) { 419 } 420 if (DEBUG) Log.v( 421 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + 422 " w=" + mLayout.width + " h=" + mLayout.height); 423 } 424 } 425 426 void attach(IWallpaperEngineWrapper wrapper) { 427 if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper); 428 mIWallpaperEngine = wrapper; 429 mCaller = wrapper.mCaller; 430 mConnection = wrapper.mConnection; 431 mWindowToken = wrapper.mWindowToken; 432 // XXX temp -- should run in size from layout (screen) mode. 433 mSurfaceHolder.setFixedSize(mIWallpaperEngine.mReqWidth, 434 mIWallpaperEngine.mReqHeight); 435 //mSurfaceHolder.setSizeFromLayout(); 436 mInitializing = true; 437 mSession = ViewRoot.getWindowSession(getMainLooper()); 438 mWindow.setSession(mSession); 439 440 onCreate(mSurfaceHolder); 441 442 mInitializing = false; 443 updateSurface(false, false); 444 } 445 446 void detach() { 447 onDestroy(); 448 if (mDestroyReportNeeded) { 449 mDestroyReportNeeded = false; 450 SurfaceHolder.Callback callbacks[]; 451 synchronized (mSurfaceHolder.mCallbacks) { 452 callbacks = new SurfaceHolder.Callback[ 453 mSurfaceHolder.mCallbacks.size()]; 454 mSurfaceHolder.mCallbacks.toArray(callbacks); 455 } 456 for (SurfaceHolder.Callback c : callbacks) { 457 c.surfaceDestroyed(mSurfaceHolder); 458 } 459 } 460 if (mCreated) { 461 try { 462 mSession.remove(mWindow); 463 } catch (RemoteException e) { 464 } 465 mSurfaceHolder.mSurface.clear(); 466 mCreated = false; 467 } 468 } 469 } 470 471 class IWallpaperEngineWrapper extends IWallpaperEngine.Stub 472 implements HandlerCaller.Callback { 473 private final HandlerCaller mCaller; 474 475 final IWallpaperConnection mConnection; 476 final IBinder mWindowToken; 477 final int mWindowType; 478 final boolean mIsPreview; 479 int mReqWidth; 480 int mReqHeight; 481 482 Engine mEngine; 483 484 IWallpaperEngineWrapper(WallpaperService context, 485 IWallpaperConnection conn, IBinder windowToken, 486 int windowType, boolean isPreview, int reqWidth, int reqHeight) { 487 mCaller = new HandlerCaller(context, this); 488 mConnection = conn; 489 mWindowToken = windowToken; 490 mWindowType = windowType; 491 mIsPreview = isPreview; 492 mReqWidth = reqWidth; 493 mReqHeight = reqHeight; 494 495 try { 496 conn.attachEngine(this); 497 } catch (RemoteException e) { 498 destroy(); 499 } 500 501 Message msg = mCaller.obtainMessage(DO_ATTACH); 502 mCaller.sendMessage(msg); 503 } 504 505 public void destroy() { 506 Message msg = mCaller.obtainMessage(DO_DETACH); 507 mCaller.sendMessage(msg); 508 } 509 510 public void executeMessage(Message message) { 511 switch (message.what) { 512 case DO_ATTACH: { 513 Engine engine = onCreateEngine(); 514 mEngine = engine; 515 engine.attach(this); 516 return; 517 } 518 case DO_DETACH: { 519 mEngine.detach(); 520 return; 521 } 522 case MSG_UPDATE_SURFACE: 523 mEngine.updateSurface(true, false); 524 break; 525 case MSG_VISIBILITY_CHANGED: 526 if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine 527 + ": " + message.arg1); 528 mEngine.onVisibilityChanged(message.arg1 != 0); 529 break; 530 case MSG_WALLPAPER_OFFSETS: { 531 float xOffset; 532 float yOffset; 533 synchronized (mEngine.mLock) { 534 xOffset = mEngine.mPendingXOffset; 535 yOffset = mEngine.mPendingYOffset; 536 mEngine.mOffsetMessageEnqueued = false; 537 } 538 if (DEBUG) Log.v(TAG, "Offsets change in " + mEngine 539 + ": " + xOffset + "," + yOffset); 540 final int availw = mReqWidth-mEngine.mCurWidth; 541 final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0; 542 final int availh = mReqHeight-mEngine.mCurHeight; 543 final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0; 544 mEngine.onOffsetsChanged(xOffset, yOffset, xPixels, yPixels); 545 } break; 546 case MSG_WINDOW_RESIZED: { 547 final boolean reportDraw = message.arg1 != 0; 548 mEngine.updateSurface(true, false); 549 if (reportDraw) { 550 try { 551 mEngine.mSession.finishDrawing(mEngine.mWindow); 552 } catch (RemoteException e) { 553 } 554 } 555 } break; 556 case MSG_TOUCH_EVENT: { 557 MotionEvent ev = (MotionEvent)message.obj; 558 synchronized (mEngine.mLock) { 559 if (mEngine.mPendingMove == ev) { 560 mEngine.mPendingMove = null; 561 } 562 } 563 mEngine.onTouchEvent(ev); 564 ev.recycle(); 565 } break; 566 default : 567 Log.w(TAG, "Unknown message type " + message.what); 568 } 569 } 570 } 571 572 /** 573 * Implements the internal {@link IWallpaperService} interface to convert 574 * incoming calls to it back to calls on an {@link WallpaperService}. 575 */ 576 class IWallpaperServiceWrapper extends IWallpaperService.Stub { 577 private final WallpaperService mTarget; 578 579 public IWallpaperServiceWrapper(WallpaperService context) { 580 mTarget = context; 581 } 582 583 public void attach(IWallpaperConnection conn, IBinder windowToken, 584 int windowType, boolean isPreview, int reqWidth, int reqHeight) { 585 new IWallpaperEngineWrapper(mTarget, conn, windowToken, 586 windowType, isPreview, reqWidth, reqHeight); 587 } 588 } 589 590 /** 591 * Implement to return the implementation of the internal accessibility 592 * service interface. Subclasses should not override. 593 */ 594 @Override 595 public final IBinder onBind(Intent intent) { 596 return new IWallpaperServiceWrapper(this); 597 } 598 599 public abstract Engine onCreateEngine(); 600} 601