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