WallpaperService.java revision cbf15048240ecf460957ac3de98a7efbccbbb754
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 * Control whether this wallpaper will receive raw touch events 216 * from the window manager as the user interacts with the window 217 * that is currently displaying the wallpaper. By default they 218 * are turned off. If enabled, the events will be received in 219 * {@link #onTouchEvent(MotionEvent)}. 220 */ 221 public void setTouchEventsEnabled(boolean enabled) { 222 mWindowFlags = enabled 223 ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) 224 : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); 225 if (mCreated) { 226 updateSurface(false, false); 227 } 228 } 229 230 /** 231 * Called once to initialize the engine. After returning, the 232 * engine's surface will be created by the framework. 233 */ 234 public void onCreate(SurfaceHolder surfaceHolder) { 235 } 236 237 /** 238 * Called right before the engine is going away. After this the 239 * surface will be destroyed and this Engine object is no longer 240 * valid. 241 */ 242 public void onDestroy() { 243 } 244 245 /** 246 * Called to inform you of the wallpaper becoming visible or 247 * hidden. <em>It is very important that a wallpaper only use 248 * CPU while it is visible.</em>. 249 */ 250 public void onVisibilityChanged(boolean visible) { 251 } 252 253 /** 254 * Called as the user performs touch-screen interaction with the 255 * window that is currently showing this wallpaper. Note that the 256 * events you receive here are driven by the actual application the 257 * user is interacting with, so if it is slow you will get viewer 258 * move events. 259 */ 260 public void onTouchEvent(MotionEvent event) { 261 } 262 263 /** 264 * Called to inform you of the wallpaper's offsets changing 265 * within its contain, corresponding to the container's 266 * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float) 267 * WallpaperManager.setWallpaperOffsets()}. 268 */ 269 public void onOffsetsChanged(float xOffset, float yOffset, 270 int xPixelOffset, int yPixelOffset) { 271 } 272 273 /** 274 * Convenience for {@link SurfaceHolder.Callback#surfaceChanged 275 * SurfaceHolder.Callback.surfaceChanged()}. 276 */ 277 public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { 278 } 279 280 /** 281 * Convenience for {@link SurfaceHolder.Callback#surfaceCreated 282 * SurfaceHolder.Callback.surfaceCreated()}. 283 */ 284 public void onSurfaceCreated(SurfaceHolder holder) { 285 } 286 287 /** 288 * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed 289 * SurfaceHolder.Callback.surfaceDestroyed()}. 290 */ 291 public void onSurfaceDestroyed(SurfaceHolder holder) { 292 } 293 294 void updateSurface(boolean forceRelayout, boolean forceReport) { 295 int myWidth = mSurfaceHolder.getRequestedWidth(); 296 if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.FILL_PARENT; 297 int myHeight = mSurfaceHolder.getRequestedHeight(); 298 if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.FILL_PARENT; 299 300 final boolean creating = !mCreated; 301 final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat(); 302 boolean sizeChanged = mWidth != myWidth || mHeight != myHeight; 303 final boolean typeChanged = mType != mSurfaceHolder.getRequestedType(); 304 final boolean flagsChanged = mCurWindowFlags != mWindowFlags; 305 if (forceRelayout || creating || formatChanged || sizeChanged 306 || typeChanged || flagsChanged) { 307 308 if (DEBUG) Log.i(TAG, "Changes: creating=" + creating 309 + " format=" + formatChanged + " size=" + sizeChanged); 310 311 try { 312 mWidth = myWidth; 313 mHeight = myHeight; 314 mFormat = mSurfaceHolder.getRequestedFormat(); 315 mType = mSurfaceHolder.getRequestedType(); 316 317 mLayout.x = 0; 318 mLayout.y = 0; 319 mLayout.width = myWidth; 320 mLayout.height = myHeight; 321 322 mLayout.format = mFormat; 323 324 mCurWindowFlags = mWindowFlags; 325 mLayout.flags = mWindowFlags 326 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 327 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 328 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 329 ; 330 331 mLayout.memoryType = mType; 332 mLayout.token = mWindowToken; 333 334 if (!mCreated) { 335 mLayout.type = WindowManager.LayoutParams.TYPE_WALLPAPER; 336 mLayout.gravity = Gravity.LEFT|Gravity.TOP; 337 mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets); 338 } 339 340 mSurfaceHolder.mSurfaceLock.lock(); 341 mDrawingAllowed = true; 342 343 final int relayoutResult = mSession.relayout( 344 mWindow, mLayout, mWidth, mHeight, 345 View.VISIBLE, false, mWinFrame, mContentInsets, 346 mVisibleInsets, mSurfaceHolder.mSurface); 347 348 if (DEBUG) Log.i(TAG, "New surface: " + mSurfaceHolder.mSurface 349 + ", frame=" + mWinFrame); 350 351 int w = mWinFrame.width(); 352 if (mCurWidth != w) { 353 sizeChanged = true; 354 mCurWidth = w; 355 } 356 int h = mWinFrame.height(); 357 if (mCurHeight != h) { 358 sizeChanged = true; 359 mCurHeight = h; 360 } 361 362 mSurfaceHolder.mSurfaceLock.unlock(); 363 364 try { 365 mDestroyReportNeeded = true; 366 367 SurfaceHolder.Callback callbacks[] = null; 368 synchronized (mSurfaceHolder.mCallbacks) { 369 final int N = mSurfaceHolder.mCallbacks.size(); 370 if (N > 0) { 371 callbacks = new SurfaceHolder.Callback[N]; 372 mSurfaceHolder.mCallbacks.toArray(callbacks); 373 } 374 } 375 376 if (!mCreated) { 377 mIsCreating = true; 378 onSurfaceCreated(mSurfaceHolder); 379 if (callbacks != null) { 380 for (SurfaceHolder.Callback c : callbacks) { 381 c.surfaceCreated(mSurfaceHolder); 382 } 383 } 384 } 385 if (forceReport || creating || formatChanged || sizeChanged) { 386 if (DEBUG) { 387 RuntimeException e = new RuntimeException(); 388 e.fillInStackTrace(); 389 Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating 390 + " formatChanged=" + formatChanged 391 + " sizeChanged=" + sizeChanged, e); 392 } 393 onSurfaceChanged(mSurfaceHolder, mFormat, 394 mCurWidth, mCurHeight); 395 if (callbacks != null) { 396 for (SurfaceHolder.Callback c : callbacks) { 397 c.surfaceChanged(mSurfaceHolder, mFormat, 398 mCurWidth, mCurHeight); 399 } 400 } 401 } 402 } finally { 403 mIsCreating = false; 404 mCreated = true; 405 if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) { 406 mSession.finishDrawing(mWindow); 407 } 408 } 409 } catch (RemoteException ex) { 410 } 411 if (DEBUG) Log.v( 412 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + 413 " w=" + mLayout.width + " h=" + mLayout.height); 414 } 415 } 416 417 void attach(IWallpaperEngineWrapper wrapper) { 418 if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper); 419 mIWallpaperEngine = wrapper; 420 mCaller = wrapper.mCaller; 421 mConnection = wrapper.mConnection; 422 mWindowToken = wrapper.mWindowToken; 423 // XXX temp -- should run in size from layout (screen) mode. 424 mSurfaceHolder.setFixedSize(mIWallpaperEngine.mReqWidth, 425 mIWallpaperEngine.mReqHeight); 426 //mSurfaceHolder.setSizeFromLayout(); 427 mInitializing = true; 428 mSession = ViewRoot.getWindowSession(getMainLooper()); 429 mWindow.setSession(mSession); 430 431 onCreate(mSurfaceHolder); 432 433 mInitializing = false; 434 updateSurface(false, false); 435 } 436 437 void detach() { 438 onDestroy(); 439 if (mDestroyReportNeeded) { 440 mDestroyReportNeeded = false; 441 SurfaceHolder.Callback callbacks[]; 442 synchronized (mSurfaceHolder.mCallbacks) { 443 callbacks = new SurfaceHolder.Callback[ 444 mSurfaceHolder.mCallbacks.size()]; 445 mSurfaceHolder.mCallbacks.toArray(callbacks); 446 } 447 for (SurfaceHolder.Callback c : callbacks) { 448 c.surfaceDestroyed(mSurfaceHolder); 449 } 450 } 451 if (mCreated) { 452 try { 453 mSession.remove(mWindow); 454 } catch (RemoteException e) { 455 } 456 mSurfaceHolder.mSurface.clear(); 457 mCreated = false; 458 } 459 } 460 } 461 462 class IWallpaperEngineWrapper extends IWallpaperEngine.Stub 463 implements HandlerCaller.Callback { 464 private final HandlerCaller mCaller; 465 466 final IWallpaperConnection mConnection; 467 final IBinder mWindowToken; 468 int mReqWidth; 469 int mReqHeight; 470 471 Engine mEngine; 472 473 IWallpaperEngineWrapper(WallpaperService context, 474 IWallpaperConnection conn, IBinder windowToken, 475 int reqWidth, int reqHeight) { 476 mCaller = new HandlerCaller(context, this); 477 mConnection = conn; 478 mWindowToken = windowToken; 479 mReqWidth = reqWidth; 480 mReqHeight = reqHeight; 481 482 try { 483 conn.attachEngine(this); 484 } catch (RemoteException e) { 485 destroy(); 486 } 487 488 Message msg = mCaller.obtainMessage(DO_ATTACH); 489 mCaller.sendMessage(msg); 490 } 491 492 public void destroy() { 493 Message msg = mCaller.obtainMessage(DO_DETACH); 494 mCaller.sendMessage(msg); 495 } 496 497 public void executeMessage(Message message) { 498 switch (message.what) { 499 case DO_ATTACH: { 500 Engine engine = onCreateEngine(); 501 mEngine = engine; 502 engine.attach(this); 503 return; 504 } 505 case DO_DETACH: { 506 mEngine.detach(); 507 return; 508 } 509 case MSG_UPDATE_SURFACE: 510 mEngine.updateSurface(true, false); 511 break; 512 case MSG_VISIBILITY_CHANGED: 513 if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine 514 + ": " + message.arg1); 515 mEngine.onVisibilityChanged(message.arg1 != 0); 516 break; 517 case MSG_WALLPAPER_OFFSETS: { 518 float xOffset; 519 float yOffset; 520 synchronized (mEngine.mLock) { 521 xOffset = mEngine.mPendingXOffset; 522 yOffset = mEngine.mPendingYOffset; 523 mEngine.mOffsetMessageEnqueued = false; 524 } 525 if (DEBUG) Log.v(TAG, "Offsets change in " + mEngine 526 + ": " + xOffset + "," + yOffset); 527 final int availw = mReqWidth-mEngine.mCurWidth; 528 final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0; 529 final int availh = mReqHeight-mEngine.mCurHeight; 530 final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0; 531 mEngine.onOffsetsChanged(xOffset, yOffset, xPixels, yPixels); 532 } break; 533 case MSG_WINDOW_RESIZED: { 534 final boolean reportDraw = message.arg1 != 0; 535 mEngine.updateSurface(true, false); 536 if (reportDraw) { 537 try { 538 mEngine.mSession.finishDrawing(mEngine.mWindow); 539 } catch (RemoteException e) { 540 } 541 } 542 } break; 543 case MSG_TOUCH_EVENT: { 544 MotionEvent ev = (MotionEvent)message.obj; 545 synchronized (mEngine.mLock) { 546 if (mEngine.mPendingMove == ev) { 547 mEngine.mPendingMove = null; 548 } 549 } 550 mEngine.onTouchEvent(ev); 551 ev.recycle(); 552 } break; 553 default : 554 Log.w(TAG, "Unknown message type " + message.what); 555 } 556 } 557 } 558 559 /** 560 * Implements the internal {@link IWallpaperService} interface to convert 561 * incoming calls to it back to calls on an {@link WallpaperService}. 562 */ 563 class IWallpaperServiceWrapper extends IWallpaperService.Stub { 564 private final WallpaperService mTarget; 565 566 public IWallpaperServiceWrapper(WallpaperService context) { 567 mTarget = context; 568 } 569 570 public void attach(IWallpaperConnection conn, 571 IBinder windowToken, int reqWidth, int reqHeight) { 572 new IWallpaperEngineWrapper( 573 mTarget, conn, windowToken, reqWidth, reqHeight); 574 } 575 } 576 577 /** 578 * Implement to return the implementation of the internal accessibility 579 * service interface. Subclasses should not override. 580 */ 581 @Override 582 public final IBinder onBind(Intent intent) { 583 return new IWallpaperServiceWrapper(this); 584 } 585 586 public abstract Engine onCreateEngine(); 587} 588