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