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