WallpaperService.java revision a2838dfc2c1c49b6638a59e82cd8758624efd660
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 android.content.res.TypedArray; 20import android.os.Build; 21import android.os.SystemProperties; 22import android.util.DisplayMetrics; 23import android.util.TypedValue; 24import android.view.ViewRootImpl; 25import android.view.WindowInsets; 26 27import com.android.internal.R; 28import com.android.internal.os.HandlerCaller; 29import com.android.internal.view.BaseIWindow; 30import com.android.internal.view.BaseSurfaceHolder; 31 32import android.annotation.SdkConstant; 33import android.annotation.SdkConstant.SdkConstantType; 34import android.app.Service; 35import android.app.WallpaperManager; 36import android.content.Context; 37import android.content.Intent; 38import android.content.res.Configuration; 39import android.graphics.PixelFormat; 40import android.graphics.Rect; 41import android.hardware.display.DisplayManager; 42import android.hardware.display.DisplayManager.DisplayListener; 43import android.os.Bundle; 44import android.os.IBinder; 45import android.os.Looper; 46import android.os.Message; 47import android.os.RemoteException; 48import android.util.Log; 49import android.view.Display; 50import android.view.Gravity; 51import android.view.IWindowSession; 52import android.view.InputChannel; 53import android.view.InputDevice; 54import android.view.InputEvent; 55import android.view.InputEventReceiver; 56import android.view.MotionEvent; 57import android.view.SurfaceHolder; 58import android.view.View; 59import android.view.ViewGroup; 60import android.view.WindowManager; 61import android.view.WindowManagerGlobal; 62 63import java.io.FileDescriptor; 64import java.io.PrintWriter; 65import java.util.ArrayList; 66 67import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; 68 69/** 70 * A wallpaper service is responsible for showing a live wallpaper behind 71 * applications that would like to sit on top of it. This service object 72 * itself does very little -- its only purpose is to generate instances of 73 * {@link Engine} as needed. Implementing a wallpaper thus 74 * involves subclassing from this, subclassing an Engine implementation, 75 * and implementing {@link #onCreateEngine()} to return a new instance of 76 * your engine. 77 */ 78public abstract class WallpaperService extends Service { 79 /** 80 * The {@link Intent} that must be declared as handled by the service. 81 * To be supported, the service must also require the 82 * {@link android.Manifest.permission#BIND_WALLPAPER} permission so 83 * that other applications can not abuse it. 84 */ 85 @SdkConstant(SdkConstantType.SERVICE_ACTION) 86 public static final String SERVICE_INTERFACE = 87 "android.service.wallpaper.WallpaperService"; 88 89 /** 90 * Name under which a WallpaperService component publishes information 91 * about itself. This meta-data must reference an XML resource containing 92 * a <code><{@link android.R.styleable#Wallpaper wallpaper}></code> 93 * tag. 94 */ 95 public static final String SERVICE_META_DATA = "android.service.wallpaper"; 96 97 static final String TAG = "WallpaperService"; 98 static final boolean DEBUG = false; 99 100 private static final int DO_ATTACH = 10; 101 private static final int DO_DETACH = 20; 102 private static final int DO_SET_DESIRED_SIZE = 30; 103 private static final int DO_SET_DISPLAY_PADDING = 40; 104 105 private static final int MSG_UPDATE_SURFACE = 10000; 106 private static final int MSG_VISIBILITY_CHANGED = 10010; 107 private static final int MSG_WALLPAPER_OFFSETS = 10020; 108 private static final int MSG_WALLPAPER_COMMAND = 10025; 109 private static final int MSG_WINDOW_RESIZED = 10030; 110 private static final int MSG_WINDOW_MOVED = 10035; 111 private static final int MSG_TOUCH_EVENT = 10040; 112 113 private final ArrayList<Engine> mActiveEngines 114 = new ArrayList<Engine>(); 115 116 static final class WallpaperCommand { 117 String action; 118 int x; 119 int y; 120 int z; 121 Bundle extras; 122 boolean sync; 123 } 124 125 /** 126 * The actual implementation of a wallpaper. A wallpaper service may 127 * have multiple instances running (for example as a real wallpaper 128 * and as a preview), each of which is represented by its own Engine 129 * instance. You must implement {@link WallpaperService#onCreateEngine()} 130 * to return your concrete Engine implementation. 131 */ 132 public class Engine { 133 IWallpaperEngineWrapper mIWallpaperEngine; 134 135 // Copies from mIWallpaperEngine. 136 HandlerCaller mCaller; 137 IWallpaperConnection mConnection; 138 IBinder mWindowToken; 139 140 boolean mInitializing = true; 141 boolean mVisible; 142 boolean mReportedVisible; 143 boolean mDestroyed; 144 145 // Current window state. 146 boolean mCreated; 147 boolean mSurfaceCreated; 148 boolean mIsCreating; 149 boolean mDrawingAllowed; 150 boolean mOffsetsChanged; 151 boolean mFixedSizeAllowed; 152 int mWidth; 153 int mHeight; 154 int mFormat; 155 int mType; 156 int mCurWidth; 157 int mCurHeight; 158 int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; 159 int mWindowPrivateFlags = 160 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS; 161 int mCurWindowFlags = mWindowFlags; 162 int mCurWindowPrivateFlags = mWindowPrivateFlags; 163 TypedValue mOutsetBottom; 164 final Rect mVisibleInsets = new Rect(); 165 final Rect mWinFrame = new Rect(); 166 final Rect mOverscanInsets = new Rect(); 167 final Rect mContentInsets = new Rect(); 168 final Rect mStableInsets = new Rect(); 169 final Rect mDispatchedOverscanInsets = new Rect(); 170 final Rect mDispatchedContentInsets = new Rect(); 171 final Rect mDispatchedStableInsets = new Rect(); 172 final Rect mFinalSystemInsets = new Rect(); 173 final Rect mFinalStableInsets = new Rect(); 174 final Configuration mConfiguration = new Configuration(); 175 176 private boolean mIsEmulator; 177 private boolean mIsCircularEmulator; 178 private boolean mWindowIsRound; 179 180 final WindowManager.LayoutParams mLayout 181 = new WindowManager.LayoutParams(); 182 IWindowSession mSession; 183 InputChannel mInputChannel; 184 185 final Object mLock = new Object(); 186 boolean mOffsetMessageEnqueued; 187 float mPendingXOffset; 188 float mPendingYOffset; 189 float mPendingXOffsetStep; 190 float mPendingYOffsetStep; 191 boolean mPendingSync; 192 MotionEvent mPendingMove; 193 194 DisplayManager mDisplayManager; 195 Display mDisplay; 196 197 final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() { 198 { 199 mRequestedFormat = PixelFormat.RGBX_8888; 200 } 201 202 @Override 203 public boolean onAllowLockCanvas() { 204 return mDrawingAllowed; 205 } 206 207 @Override 208 public void onRelayoutContainer() { 209 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE); 210 mCaller.sendMessage(msg); 211 } 212 213 @Override 214 public void onUpdateSurface() { 215 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE); 216 mCaller.sendMessage(msg); 217 } 218 219 public boolean isCreating() { 220 return mIsCreating; 221 } 222 223 @Override 224 public void setFixedSize(int width, int height) { 225 if (!mFixedSizeAllowed) { 226 // Regular apps can't do this. It can only work for 227 // certain designs of window animations, so you can't 228 // rely on it. 229 throw new UnsupportedOperationException( 230 "Wallpapers currently only support sizing from layout"); 231 } 232 super.setFixedSize(width, height); 233 } 234 235 public void setKeepScreenOn(boolean screenOn) { 236 throw new UnsupportedOperationException( 237 "Wallpapers do not support keep screen on"); 238 } 239 240 }; 241 242 final class WallpaperInputEventReceiver extends InputEventReceiver { 243 public WallpaperInputEventReceiver(InputChannel inputChannel, Looper looper) { 244 super(inputChannel, looper); 245 } 246 247 @Override 248 public void onInputEvent(InputEvent event) { 249 boolean handled = false; 250 try { 251 if (event instanceof MotionEvent 252 && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { 253 MotionEvent dup = MotionEvent.obtainNoHistory((MotionEvent)event); 254 dispatchPointer(dup); 255 handled = true; 256 } 257 } finally { 258 finishInputEvent(event, handled); 259 } 260 } 261 } 262 WallpaperInputEventReceiver mInputEventReceiver; 263 264 final BaseIWindow mWindow = new BaseIWindow() { 265 @Override 266 public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, 267 Rect visibleInsets, Rect stableInsets, boolean reportDraw, 268 Configuration newConfig) { 269 Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED, 270 reportDraw ? 1 : 0); 271 mCaller.sendMessage(msg); 272 } 273 274 @Override 275 public void moved(int newX, int newY) { 276 Message msg = mCaller.obtainMessageII(MSG_WINDOW_MOVED, newX, newY); 277 mCaller.sendMessage(msg); 278 } 279 280 @Override 281 public void dispatchAppVisibility(boolean visible) { 282 // We don't do this in preview mode; we'll let the preview 283 // activity tell us when to run. 284 if (!mIWallpaperEngine.mIsPreview) { 285 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 286 visible ? 1 : 0); 287 mCaller.sendMessage(msg); 288 } 289 } 290 291 @Override 292 public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, 293 boolean sync) { 294 synchronized (mLock) { 295 if (DEBUG) Log.v(TAG, "Dispatch wallpaper offsets: " + x + ", " + y); 296 mPendingXOffset = x; 297 mPendingYOffset = y; 298 mPendingXOffsetStep = xStep; 299 mPendingYOffsetStep = yStep; 300 if (sync) { 301 mPendingSync = true; 302 } 303 if (!mOffsetMessageEnqueued) { 304 mOffsetMessageEnqueued = true; 305 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS); 306 mCaller.sendMessage(msg); 307 } 308 } 309 } 310 311 @Override 312 public void dispatchWallpaperCommand(String action, int x, int y, 313 int z, Bundle extras, boolean sync) { 314 synchronized (mLock) { 315 if (DEBUG) Log.v(TAG, "Dispatch wallpaper command: " + x + ", " + y); 316 WallpaperCommand cmd = new WallpaperCommand(); 317 cmd.action = action; 318 cmd.x = x; 319 cmd.y = y; 320 cmd.z = z; 321 cmd.extras = extras; 322 cmd.sync = sync; 323 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_COMMAND); 324 msg.obj = cmd; 325 mCaller.sendMessage(msg); 326 } 327 } 328 }; 329 330 /** 331 * Provides access to the surface in which this wallpaper is drawn. 332 */ 333 public SurfaceHolder getSurfaceHolder() { 334 return mSurfaceHolder; 335 } 336 337 /** 338 * Convenience for {@link WallpaperManager#getDesiredMinimumWidth() 339 * WallpaperManager.getDesiredMinimumWidth()}, returning the width 340 * that the system would like this wallpaper to run in. 341 */ 342 public int getDesiredMinimumWidth() { 343 return mIWallpaperEngine.mReqWidth; 344 } 345 346 /** 347 * Convenience for {@link WallpaperManager#getDesiredMinimumHeight() 348 * WallpaperManager.getDesiredMinimumHeight()}, returning the height 349 * that the system would like this wallpaper to run in. 350 */ 351 public int getDesiredMinimumHeight() { 352 return mIWallpaperEngine.mReqHeight; 353 } 354 355 /** 356 * Return whether the wallpaper is currently visible to the user, 357 * this is the last value supplied to 358 * {@link #onVisibilityChanged(boolean)}. 359 */ 360 public boolean isVisible() { 361 return mReportedVisible; 362 } 363 364 /** 365 * Returns true if this engine is running in preview mode -- that is, 366 * it is being shown to the user before they select it as the actual 367 * wallpaper. 368 */ 369 public boolean isPreview() { 370 return mIWallpaperEngine.mIsPreview; 371 } 372 373 /** 374 * Control whether this wallpaper will receive raw touch events 375 * from the window manager as the user interacts with the window 376 * that is currently displaying the wallpaper. By default they 377 * are turned off. If enabled, the events will be received in 378 * {@link #onTouchEvent(MotionEvent)}. 379 */ 380 public void setTouchEventsEnabled(boolean enabled) { 381 mWindowFlags = enabled 382 ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) 383 : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); 384 if (mCreated) { 385 updateSurface(false, false, false); 386 } 387 } 388 389 /** 390 * Control whether this wallpaper will receive notifications when the wallpaper 391 * has been scrolled. By default, wallpapers will receive notifications, although 392 * the default static image wallpapers do not. It is a performance optimization to 393 * set this to false. 394 * 395 * @param enabled whether the wallpaper wants to receive offset notifications 396 */ 397 public void setOffsetNotificationsEnabled(boolean enabled) { 398 mWindowPrivateFlags = enabled 399 ? (mWindowPrivateFlags | 400 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) 401 : (mWindowPrivateFlags & 402 ~WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS); 403 if (mCreated) { 404 updateSurface(false, false, false); 405 } 406 } 407 408 /** {@hide} */ 409 public void setFixedSizeAllowed(boolean allowed) { 410 mFixedSizeAllowed = allowed; 411 } 412 413 /** 414 * Called once to initialize the engine. After returning, the 415 * engine's surface will be created by the framework. 416 */ 417 public void onCreate(SurfaceHolder surfaceHolder) { 418 } 419 420 /** 421 * Called right before the engine is going away. After this the 422 * surface will be destroyed and this Engine object is no longer 423 * valid. 424 */ 425 public void onDestroy() { 426 } 427 428 /** 429 * Called to inform you of the wallpaper becoming visible or 430 * hidden. <em>It is very important that a wallpaper only use 431 * CPU while it is visible.</em>. 432 */ 433 public void onVisibilityChanged(boolean visible) { 434 } 435 436 /** 437 * Called with the current insets that are in effect for the wallpaper. 438 * This gives you the part of the overall wallpaper surface that will 439 * generally be visible to the user (ignoring position offsets applied to it). 440 * 441 * @param insets Insets to apply. 442 */ 443 public void onApplyWindowInsets(WindowInsets insets) { 444 } 445 446 /** 447 * Called as the user performs touch-screen interaction with the 448 * window that is currently showing this wallpaper. Note that the 449 * events you receive here are driven by the actual application the 450 * user is interacting with, so if it is slow you will get fewer 451 * move events. 452 */ 453 public void onTouchEvent(MotionEvent event) { 454 } 455 456 /** 457 * Called to inform you of the wallpaper's offsets changing 458 * within its contain, corresponding to the container's 459 * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float) 460 * WallpaperManager.setWallpaperOffsets()}. 461 */ 462 public void onOffsetsChanged(float xOffset, float yOffset, 463 float xOffsetStep, float yOffsetStep, 464 int xPixelOffset, int yPixelOffset) { 465 } 466 467 /** 468 * Process a command that was sent to the wallpaper with 469 * {@link WallpaperManager#sendWallpaperCommand}. 470 * The default implementation does nothing, and always returns null 471 * as the result. 472 * 473 * @param action The name of the command to perform. This tells you 474 * what to do and how to interpret the rest of the arguments. 475 * @param x Generic integer parameter. 476 * @param y Generic integer parameter. 477 * @param z Generic integer parameter. 478 * @param extras Any additional parameters. 479 * @param resultRequested If true, the caller is requesting that 480 * a result, appropriate for the command, be returned back. 481 * @return If returning a result, create a Bundle and place the 482 * result data in to it. Otherwise return null. 483 */ 484 public Bundle onCommand(String action, int x, int y, int z, 485 Bundle extras, boolean resultRequested) { 486 return null; 487 } 488 489 /** 490 * Called when an application has changed the desired virtual size of 491 * the wallpaper. 492 */ 493 public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) { 494 } 495 496 /** 497 * Convenience for {@link SurfaceHolder.Callback#surfaceChanged 498 * SurfaceHolder.Callback.surfaceChanged()}. 499 */ 500 public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { 501 } 502 503 /** 504 * Convenience for {@link SurfaceHolder.Callback2#surfaceRedrawNeeded 505 * SurfaceHolder.Callback.surfaceRedrawNeeded()}. 506 */ 507 public void onSurfaceRedrawNeeded(SurfaceHolder holder) { 508 } 509 510 /** 511 * Convenience for {@link SurfaceHolder.Callback#surfaceCreated 512 * SurfaceHolder.Callback.surfaceCreated()}. 513 */ 514 public void onSurfaceCreated(SurfaceHolder holder) { 515 } 516 517 /** 518 * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed 519 * SurfaceHolder.Callback.surfaceDestroyed()}. 520 */ 521 public void onSurfaceDestroyed(SurfaceHolder holder) { 522 } 523 524 protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) { 525 out.print(prefix); out.print("mInitializing="); out.print(mInitializing); 526 out.print(" mDestroyed="); out.println(mDestroyed); 527 out.print(prefix); out.print("mVisible="); out.print(mVisible); 528 out.print(" mReportedVisible="); out.println(mReportedVisible); 529 out.print(prefix); out.print("mDisplay="); out.println(mDisplay); 530 out.print(prefix); out.print("mCreated="); out.print(mCreated); 531 out.print(" mSurfaceCreated="); out.print(mSurfaceCreated); 532 out.print(" mIsCreating="); out.print(mIsCreating); 533 out.print(" mDrawingAllowed="); out.println(mDrawingAllowed); 534 out.print(prefix); out.print("mWidth="); out.print(mWidth); 535 out.print(" mCurWidth="); out.print(mCurWidth); 536 out.print(" mHeight="); out.print(mHeight); 537 out.print(" mCurHeight="); out.println(mCurHeight); 538 out.print(prefix); out.print("mType="); out.print(mType); 539 out.print(" mWindowFlags="); out.print(mWindowFlags); 540 out.print(" mCurWindowFlags="); out.println(mCurWindowFlags); 541 out.print(" mWindowPrivateFlags="); out.print(mWindowPrivateFlags); 542 out.print(" mCurWindowPrivateFlags="); out.println(mCurWindowPrivateFlags); 543 out.print(prefix); out.print("mVisibleInsets="); 544 out.print(mVisibleInsets.toShortString()); 545 out.print(" mWinFrame="); out.print(mWinFrame.toShortString()); 546 out.print(" mContentInsets="); out.println(mContentInsets.toShortString()); 547 out.print(prefix); out.print("mConfiguration="); out.println(mConfiguration); 548 out.print(prefix); out.print("mLayout="); out.println(mLayout); 549 synchronized (mLock) { 550 out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset); 551 out.print(" mPendingXOffset="); out.println(mPendingXOffset); 552 out.print(prefix); out.print("mPendingXOffsetStep="); 553 out.print(mPendingXOffsetStep); 554 out.print(" mPendingXOffsetStep="); out.println(mPendingXOffsetStep); 555 out.print(prefix); out.print("mOffsetMessageEnqueued="); 556 out.print(mOffsetMessageEnqueued); 557 out.print(" mPendingSync="); out.println(mPendingSync); 558 if (mPendingMove != null) { 559 out.print(prefix); out.print("mPendingMove="); out.println(mPendingMove); 560 } 561 } 562 } 563 564 private void dispatchPointer(MotionEvent event) { 565 if (event.isTouchEvent()) { 566 synchronized (mLock) { 567 if (event.getAction() == MotionEvent.ACTION_MOVE) { 568 mPendingMove = event; 569 } else { 570 mPendingMove = null; 571 } 572 } 573 Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event); 574 mCaller.sendMessage(msg); 575 } else { 576 event.recycle(); 577 } 578 } 579 580 void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) { 581 if (mDestroyed) { 582 Log.w(TAG, "Ignoring updateSurface: destroyed"); 583 } 584 585 boolean fixedSize = false; 586 int myWidth = mSurfaceHolder.getRequestedWidth(); 587 if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT; 588 else fixedSize = true; 589 int myHeight = mSurfaceHolder.getRequestedHeight(); 590 if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT; 591 else fixedSize = true; 592 593 final boolean creating = !mCreated; 594 final boolean surfaceCreating = !mSurfaceCreated; 595 final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat(); 596 boolean sizeChanged = mWidth != myWidth || mHeight != myHeight; 597 boolean insetsChanged = !mCreated; 598 final boolean typeChanged = mType != mSurfaceHolder.getRequestedType(); 599 final boolean flagsChanged = mCurWindowFlags != mWindowFlags || 600 mCurWindowPrivateFlags != mWindowPrivateFlags; 601 if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged 602 || typeChanged || flagsChanged || redrawNeeded 603 || !mIWallpaperEngine.mShownReported) { 604 605 if (DEBUG) Log.v(TAG, "Changes: creating=" + creating 606 + " format=" + formatChanged + " size=" + sizeChanged); 607 608 try { 609 mWidth = myWidth; 610 mHeight = myHeight; 611 mFormat = mSurfaceHolder.getRequestedFormat(); 612 mType = mSurfaceHolder.getRequestedType(); 613 614 mLayout.x = 0; 615 mLayout.y = 0; 616 mLayout.width = myWidth; 617 mLayout.height = myHeight; 618 619 mLayout.format = mFormat; 620 621 mCurWindowFlags = mWindowFlags; 622 mLayout.flags = mWindowFlags 623 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 624 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 625 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 626 ; 627 mCurWindowPrivateFlags = mWindowPrivateFlags; 628 mLayout.privateFlags = mWindowPrivateFlags; 629 630 mLayout.memoryType = mType; 631 mLayout.token = mWindowToken; 632 633 if (!mCreated) { 634 // Retrieve watch round and outset info 635 final WindowManager windowService = (WindowManager)getSystemService( 636 Context.WINDOW_SERVICE); 637 TypedArray windowStyle = obtainStyledAttributes( 638 com.android.internal.R.styleable.Window); 639 final Display display = windowService.getDefaultDisplay(); 640 final boolean shouldUseBottomOutset = 641 display.getDisplayId() == Display.DEFAULT_DISPLAY; 642 if (shouldUseBottomOutset && windowStyle.hasValue( 643 R.styleable.Window_windowOutsetBottom)) { 644 if (mOutsetBottom == null) mOutsetBottom = new TypedValue(); 645 windowStyle.getValue(R.styleable.Window_windowOutsetBottom, 646 mOutsetBottom); 647 } else { 648 mOutsetBottom = null; 649 } 650 mWindowIsRound = getResources().getBoolean( 651 com.android.internal.R.bool.config_windowIsRound); 652 windowStyle.recycle(); 653 654 // detect emulator 655 mIsEmulator = Build.HARDWARE.contains("goldfish"); 656 mIsCircularEmulator = SystemProperties.getBoolean( 657 ViewRootImpl.PROPERTY_EMULATOR_CIRCULAR, false); 658 659 // Add window 660 mLayout.type = mIWallpaperEngine.mWindowType; 661 mLayout.gravity = Gravity.START|Gravity.TOP; 662 mLayout.setTitle(WallpaperService.this.getClass().getName()); 663 mLayout.windowAnimations = 664 com.android.internal.R.style.Animation_Wallpaper; 665 mInputChannel = new InputChannel(); 666 if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE, 667 Display.DEFAULT_DISPLAY, mContentInsets, mInputChannel) < 0) { 668 Log.w(TAG, "Failed to add window while updating wallpaper surface."); 669 return; 670 } 671 mCreated = true; 672 673 mInputEventReceiver = new WallpaperInputEventReceiver( 674 mInputChannel, Looper.myLooper()); 675 } 676 677 mSurfaceHolder.mSurfaceLock.lock(); 678 mDrawingAllowed = true; 679 680 if (!fixedSize) { 681 mLayout.surfaceInsets.set(mIWallpaperEngine.mDisplayPadding); 682 } else { 683 mLayout.surfaceInsets.set(0, 0, 0, 0); 684 } 685 final int relayoutResult = mSession.relayout( 686 mWindow, mWindow.mSeq, mLayout, mWidth, mHeight, 687 View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets, 688 mVisibleInsets, mStableInsets, mConfiguration, mSurfaceHolder.mSurface); 689 690 if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface 691 + ", frame=" + mWinFrame); 692 693 int w = mWinFrame.width() + mOverscanInsets.left + mOverscanInsets.right; 694 int h = mWinFrame.height() + mOverscanInsets.top + mOverscanInsets.bottom; 695 696 if (!fixedSize) { 697 final Rect padding = mIWallpaperEngine.mDisplayPadding; 698 w += padding.left + padding.right; 699 h += padding.top + padding.bottom; 700 mOverscanInsets.left += padding.left; 701 mOverscanInsets.top += padding.top; 702 mOverscanInsets.right += padding.right; 703 mOverscanInsets.bottom += padding.bottom; 704 mContentInsets.left += padding.left; 705 mContentInsets.top += padding.top; 706 mContentInsets.right += padding.right; 707 mContentInsets.bottom += padding.bottom; 708 mStableInsets.left += padding.left; 709 mStableInsets.top += padding.top; 710 mStableInsets.right += padding.right; 711 mStableInsets.bottom += padding.bottom; 712 } 713 714 if (mCurWidth != w) { 715 sizeChanged = true; 716 mCurWidth = w; 717 } 718 if (mCurHeight != h) { 719 sizeChanged = true; 720 mCurHeight = h; 721 } 722 723 insetsChanged |= !mDispatchedOverscanInsets.equals(mOverscanInsets); 724 insetsChanged |= !mDispatchedContentInsets.equals(mContentInsets); 725 insetsChanged |= !mDispatchedStableInsets.equals(mStableInsets); 726 727 mSurfaceHolder.setSurfaceFrameSize(w, h); 728 mSurfaceHolder.mSurfaceLock.unlock(); 729 730 if (!mSurfaceHolder.mSurface.isValid()) { 731 reportSurfaceDestroyed(); 732 if (DEBUG) Log.v(TAG, "Layout: Surface destroyed"); 733 return; 734 } 735 736 boolean didSurface = false; 737 738 try { 739 mSurfaceHolder.ungetCallbacks(); 740 741 if (surfaceCreating) { 742 mIsCreating = true; 743 didSurface = true; 744 if (DEBUG) Log.v(TAG, "onSurfaceCreated(" 745 + mSurfaceHolder + "): " + this); 746 onSurfaceCreated(mSurfaceHolder); 747 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 748 if (callbacks != null) { 749 for (SurfaceHolder.Callback c : callbacks) { 750 c.surfaceCreated(mSurfaceHolder); 751 } 752 } 753 } 754 755 redrawNeeded |= creating || (relayoutResult 756 & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0; 757 758 if (forceReport || creating || surfaceCreating 759 || formatChanged || sizeChanged) { 760 if (DEBUG) { 761 RuntimeException e = new RuntimeException(); 762 e.fillInStackTrace(); 763 Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating 764 + " formatChanged=" + formatChanged 765 + " sizeChanged=" + sizeChanged, e); 766 } 767 if (DEBUG) Log.v(TAG, "onSurfaceChanged(" 768 + mSurfaceHolder + ", " + mFormat 769 + ", " + mCurWidth + ", " + mCurHeight 770 + "): " + this); 771 didSurface = true; 772 onSurfaceChanged(mSurfaceHolder, mFormat, 773 mCurWidth, mCurHeight); 774 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 775 if (callbacks != null) { 776 for (SurfaceHolder.Callback c : callbacks) { 777 c.surfaceChanged(mSurfaceHolder, mFormat, 778 mCurWidth, mCurHeight); 779 } 780 } 781 } 782 783 if (insetsChanged) { 784 mDispatchedOverscanInsets.set(mOverscanInsets); 785 mDispatchedContentInsets.set(mContentInsets); 786 mDispatchedStableInsets.set(mStableInsets); 787 final boolean isRound = (mIsEmulator && mIsCircularEmulator) 788 || mWindowIsRound; 789 mFinalSystemInsets.set(mDispatchedOverscanInsets); 790 mFinalStableInsets.set(mDispatchedStableInsets); 791 if (mOutsetBottom != null) { 792 final DisplayMetrics metrics = getResources().getDisplayMetrics(); 793 mFinalSystemInsets.bottom = 794 ( (int) mOutsetBottom.getDimension(metrics) ) 795 + mIWallpaperEngine.mDisplayPadding.bottom; 796 } 797 WindowInsets insets = new WindowInsets(mFinalSystemInsets, 798 null, mFinalStableInsets, isRound); 799 onApplyWindowInsets(insets); 800 } 801 802 if (redrawNeeded) { 803 onSurfaceRedrawNeeded(mSurfaceHolder); 804 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 805 if (callbacks != null) { 806 for (SurfaceHolder.Callback c : callbacks) { 807 if (c instanceof SurfaceHolder.Callback2) { 808 ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( 809 mSurfaceHolder); 810 } 811 } 812 } 813 } 814 815 if (didSurface && !mReportedVisible) { 816 // This wallpaper is currently invisible, but its 817 // surface has changed. At this point let's tell it 818 // again that it is invisible in case the report about 819 // the surface caused it to start running. We really 820 // don't want wallpapers running when not visible. 821 if (mIsCreating) { 822 // Some wallpapers will ignore this call if they 823 // had previously been told they were invisble, 824 // so if we are creating a new surface then toggle 825 // the state to get them to notice. 826 if (DEBUG) Log.v(TAG, "onVisibilityChanged(true) at surface: " 827 + this); 828 onVisibilityChanged(true); 829 } 830 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false) at surface: " 831 + this); 832 onVisibilityChanged(false); 833 } 834 835 } finally { 836 mIsCreating = false; 837 mSurfaceCreated = true; 838 if (redrawNeeded) { 839 mSession.finishDrawing(mWindow); 840 } 841 mIWallpaperEngine.reportShown(); 842 } 843 } catch (RemoteException ex) { 844 } 845 if (DEBUG) Log.v( 846 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + 847 " w=" + mLayout.width + " h=" + mLayout.height); 848 } 849 } 850 851 void attach(IWallpaperEngineWrapper wrapper) { 852 if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper); 853 if (mDestroyed) { 854 return; 855 } 856 857 mIWallpaperEngine = wrapper; 858 mCaller = wrapper.mCaller; 859 mConnection = wrapper.mConnection; 860 mWindowToken = wrapper.mWindowToken; 861 mSurfaceHolder.setSizeFromLayout(); 862 mInitializing = true; 863 mSession = WindowManagerGlobal.getWindowSession(); 864 865 mWindow.setSession(mSession); 866 867 mDisplayManager = (DisplayManager)getSystemService(Context.DISPLAY_SERVICE); 868 mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler()); 869 mDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); 870 871 if (DEBUG) Log.v(TAG, "onCreate(): " + this); 872 onCreate(mSurfaceHolder); 873 874 mInitializing = false; 875 mReportedVisible = false; 876 updateSurface(false, false, false); 877 } 878 879 void doDesiredSizeChanged(int desiredWidth, int desiredHeight) { 880 if (!mDestroyed) { 881 if (DEBUG) Log.v(TAG, "onDesiredSizeChanged(" 882 + desiredWidth + "," + desiredHeight + "): " + this); 883 mIWallpaperEngine.mReqWidth = desiredWidth; 884 mIWallpaperEngine.mReqHeight = desiredHeight; 885 onDesiredSizeChanged(desiredWidth, desiredHeight); 886 doOffsetsChanged(true); 887 } 888 } 889 890 void doDisplayPaddingChanged(Rect padding) { 891 if (!mDestroyed) { 892 if (DEBUG) Log.v(TAG, "onDisplayPaddingChanged(" + padding + "): " + this); 893 if (!mIWallpaperEngine.mDisplayPadding.equals(padding)) { 894 mIWallpaperEngine.mDisplayPadding.set(padding); 895 updateSurface(true, false, false); 896 } 897 } 898 } 899 900 void doVisibilityChanged(boolean visible) { 901 if (!mDestroyed) { 902 mVisible = visible; 903 reportVisibility(); 904 } 905 } 906 907 void reportVisibility() { 908 if (!mDestroyed) { 909 boolean visible = mVisible 910 & mDisplay != null && mDisplay.getState() != Display.STATE_OFF; 911 if (mReportedVisible != visible) { 912 mReportedVisible = visible; 913 if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible 914 + "): " + this); 915 if (visible) { 916 // If becoming visible, in preview mode the surface 917 // may have been destroyed so now we need to make 918 // sure it is re-created. 919 doOffsetsChanged(false); 920 updateSurface(false, false, false); 921 } 922 onVisibilityChanged(visible); 923 } 924 } 925 } 926 927 void doOffsetsChanged(boolean always) { 928 if (mDestroyed) { 929 return; 930 } 931 932 if (!always && !mOffsetsChanged) { 933 return; 934 } 935 936 float xOffset; 937 float yOffset; 938 float xOffsetStep; 939 float yOffsetStep; 940 boolean sync; 941 synchronized (mLock) { 942 xOffset = mPendingXOffset; 943 yOffset = mPendingYOffset; 944 xOffsetStep = mPendingXOffsetStep; 945 yOffsetStep = mPendingYOffsetStep; 946 sync = mPendingSync; 947 mPendingSync = false; 948 mOffsetMessageEnqueued = false; 949 } 950 951 if (mSurfaceCreated) { 952 if (mReportedVisible) { 953 if (DEBUG) Log.v(TAG, "Offsets change in " + this 954 + ": " + xOffset + "," + yOffset); 955 final int availw = mIWallpaperEngine.mReqWidth-mCurWidth; 956 final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0; 957 final int availh = mIWallpaperEngine.mReqHeight-mCurHeight; 958 final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0; 959 onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels); 960 } else { 961 mOffsetsChanged = true; 962 } 963 } 964 965 if (sync) { 966 try { 967 if (DEBUG) Log.v(TAG, "Reporting offsets change complete"); 968 mSession.wallpaperOffsetsComplete(mWindow.asBinder()); 969 } catch (RemoteException e) { 970 } 971 } 972 } 973 974 void doCommand(WallpaperCommand cmd) { 975 Bundle result; 976 if (!mDestroyed) { 977 result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z, 978 cmd.extras, cmd.sync); 979 } else { 980 result = null; 981 } 982 if (cmd.sync) { 983 try { 984 if (DEBUG) Log.v(TAG, "Reporting command complete"); 985 mSession.wallpaperCommandComplete(mWindow.asBinder(), result); 986 } catch (RemoteException e) { 987 } 988 } 989 } 990 991 void reportSurfaceDestroyed() { 992 if (mSurfaceCreated) { 993 mSurfaceCreated = false; 994 mSurfaceHolder.ungetCallbacks(); 995 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 996 if (callbacks != null) { 997 for (SurfaceHolder.Callback c : callbacks) { 998 c.surfaceDestroyed(mSurfaceHolder); 999 } 1000 } 1001 if (DEBUG) Log.v(TAG, "onSurfaceDestroyed(" 1002 + mSurfaceHolder + "): " + this); 1003 onSurfaceDestroyed(mSurfaceHolder); 1004 } 1005 } 1006 1007 void detach() { 1008 if (mDestroyed) { 1009 return; 1010 } 1011 1012 mDestroyed = true; 1013 1014 if (mDisplayManager != null) { 1015 mDisplayManager.unregisterDisplayListener(mDisplayListener); 1016 } 1017 1018 if (mVisible) { 1019 mVisible = false; 1020 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this); 1021 onVisibilityChanged(false); 1022 } 1023 1024 reportSurfaceDestroyed(); 1025 1026 if (DEBUG) Log.v(TAG, "onDestroy(): " + this); 1027 onDestroy(); 1028 1029 if (mCreated) { 1030 try { 1031 if (DEBUG) Log.v(TAG, "Removing window and destroying surface " 1032 + mSurfaceHolder.getSurface() + " of: " + this); 1033 1034 if (mInputEventReceiver != null) { 1035 mInputEventReceiver.dispose(); 1036 mInputEventReceiver = null; 1037 } 1038 1039 mSession.remove(mWindow); 1040 } catch (RemoteException e) { 1041 } 1042 mSurfaceHolder.mSurface.release(); 1043 mCreated = false; 1044 1045 // Dispose the input channel after removing the window so the Window Manager 1046 // doesn't interpret the input channel being closed as an abnormal termination. 1047 if (mInputChannel != null) { 1048 mInputChannel.dispose(); 1049 mInputChannel = null; 1050 } 1051 } 1052 } 1053 1054 private final DisplayListener mDisplayListener = new DisplayListener() { 1055 @Override 1056 public void onDisplayChanged(int displayId) { 1057 if (mDisplay.getDisplayId() == displayId) { 1058 reportVisibility(); 1059 } 1060 } 1061 1062 @Override 1063 public void onDisplayRemoved(int displayId) { 1064 } 1065 1066 @Override 1067 public void onDisplayAdded(int displayId) { 1068 } 1069 }; 1070 } 1071 1072 class IWallpaperEngineWrapper extends IWallpaperEngine.Stub 1073 implements HandlerCaller.Callback { 1074 private final HandlerCaller mCaller; 1075 1076 final IWallpaperConnection mConnection; 1077 final IBinder mWindowToken; 1078 final int mWindowType; 1079 final boolean mIsPreview; 1080 boolean mShownReported; 1081 int mReqWidth; 1082 int mReqHeight; 1083 final Rect mDisplayPadding = new Rect(); 1084 1085 Engine mEngine; 1086 1087 IWallpaperEngineWrapper(WallpaperService context, 1088 IWallpaperConnection conn, IBinder windowToken, 1089 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) { 1090 mCaller = new HandlerCaller(context, context.getMainLooper(), this, true); 1091 mConnection = conn; 1092 mWindowToken = windowToken; 1093 mWindowType = windowType; 1094 mIsPreview = isPreview; 1095 mReqWidth = reqWidth; 1096 mReqHeight = reqHeight; 1097 mDisplayPadding.set(padding); 1098 1099 Message msg = mCaller.obtainMessage(DO_ATTACH); 1100 mCaller.sendMessage(msg); 1101 } 1102 1103 public void setDesiredSize(int width, int height) { 1104 Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height); 1105 mCaller.sendMessage(msg); 1106 } 1107 1108 public void setDisplayPadding(Rect padding) { 1109 Message msg = mCaller.obtainMessageO(DO_SET_DISPLAY_PADDING, padding); 1110 mCaller.sendMessage(msg); 1111 } 1112 1113 public void setVisibility(boolean visible) { 1114 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 1115 visible ? 1 : 0); 1116 mCaller.sendMessage(msg); 1117 } 1118 1119 public void dispatchPointer(MotionEvent event) { 1120 if (mEngine != null) { 1121 mEngine.dispatchPointer(event); 1122 } else { 1123 event.recycle(); 1124 } 1125 } 1126 1127 public void dispatchWallpaperCommand(String action, int x, int y, 1128 int z, Bundle extras) { 1129 if (mEngine != null) { 1130 mEngine.mWindow.dispatchWallpaperCommand(action, x, y, z, extras, false); 1131 } 1132 } 1133 1134 public void reportShown() { 1135 if (!mShownReported) { 1136 mShownReported = true; 1137 try { 1138 mConnection.engineShown(this); 1139 } catch (RemoteException e) { 1140 Log.w(TAG, "Wallpaper host disappeared", e); 1141 return; 1142 } 1143 } 1144 } 1145 1146 public void destroy() { 1147 Message msg = mCaller.obtainMessage(DO_DETACH); 1148 mCaller.sendMessage(msg); 1149 } 1150 1151 public void executeMessage(Message message) { 1152 switch (message.what) { 1153 case DO_ATTACH: { 1154 try { 1155 mConnection.attachEngine(this); 1156 } catch (RemoteException e) { 1157 Log.w(TAG, "Wallpaper host disappeared", e); 1158 return; 1159 } 1160 Engine engine = onCreateEngine(); 1161 mEngine = engine; 1162 mActiveEngines.add(engine); 1163 engine.attach(this); 1164 return; 1165 } 1166 case DO_DETACH: { 1167 mActiveEngines.remove(mEngine); 1168 mEngine.detach(); 1169 return; 1170 } 1171 case DO_SET_DESIRED_SIZE: { 1172 mEngine.doDesiredSizeChanged(message.arg1, message.arg2); 1173 return; 1174 } 1175 case DO_SET_DISPLAY_PADDING: { 1176 mEngine.doDisplayPaddingChanged((Rect) message.obj); 1177 } 1178 case MSG_UPDATE_SURFACE: 1179 mEngine.updateSurface(true, false, false); 1180 break; 1181 case MSG_VISIBILITY_CHANGED: 1182 if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine 1183 + ": " + message.arg1); 1184 mEngine.doVisibilityChanged(message.arg1 != 0); 1185 break; 1186 case MSG_WALLPAPER_OFFSETS: { 1187 mEngine.doOffsetsChanged(true); 1188 } break; 1189 case MSG_WALLPAPER_COMMAND: { 1190 WallpaperCommand cmd = (WallpaperCommand)message.obj; 1191 mEngine.doCommand(cmd); 1192 } break; 1193 case MSG_WINDOW_RESIZED: { 1194 final boolean reportDraw = message.arg1 != 0; 1195 mEngine.updateSurface(true, false, reportDraw); 1196 mEngine.doOffsetsChanged(true); 1197 } break; 1198 case MSG_WINDOW_MOVED: { 1199 // Do nothing. What does it mean for a Wallpaper to move? 1200 } break; 1201 case MSG_TOUCH_EVENT: { 1202 boolean skip = false; 1203 MotionEvent ev = (MotionEvent)message.obj; 1204 if (ev.getAction() == MotionEvent.ACTION_MOVE) { 1205 synchronized (mEngine.mLock) { 1206 if (mEngine.mPendingMove == ev) { 1207 mEngine.mPendingMove = null; 1208 } else { 1209 // this is not the motion event we are looking for.... 1210 skip = true; 1211 } 1212 } 1213 } 1214 if (!skip) { 1215 if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev); 1216 mEngine.onTouchEvent(ev); 1217 } 1218 ev.recycle(); 1219 } break; 1220 default : 1221 Log.w(TAG, "Unknown message type " + message.what); 1222 } 1223 } 1224 } 1225 1226 /** 1227 * Implements the internal {@link IWallpaperService} interface to convert 1228 * incoming calls to it back to calls on an {@link WallpaperService}. 1229 */ 1230 class IWallpaperServiceWrapper extends IWallpaperService.Stub { 1231 private final WallpaperService mTarget; 1232 1233 public IWallpaperServiceWrapper(WallpaperService context) { 1234 mTarget = context; 1235 } 1236 1237 @Override 1238 public void attach(IWallpaperConnection conn, IBinder windowToken, 1239 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) { 1240 new IWallpaperEngineWrapper(mTarget, conn, windowToken, 1241 windowType, isPreview, reqWidth, reqHeight, padding); 1242 } 1243 } 1244 1245 @Override 1246 public void onCreate() { 1247 super.onCreate(); 1248 } 1249 1250 @Override 1251 public void onDestroy() { 1252 super.onDestroy(); 1253 for (int i=0; i<mActiveEngines.size(); i++) { 1254 mActiveEngines.get(i).detach(); 1255 } 1256 mActiveEngines.clear(); 1257 } 1258 1259 /** 1260 * Implement to return the implementation of the internal accessibility 1261 * service interface. Subclasses should not override. 1262 */ 1263 @Override 1264 public final IBinder onBind(Intent intent) { 1265 return new IWallpaperServiceWrapper(this); 1266 } 1267 1268 /** 1269 * Must be implemented to return a new instance of the wallpaper's engine. 1270 * Note that multiple instances may be active at the same time, such as 1271 * when the wallpaper is currently set as the active wallpaper and the user 1272 * is in the wallpaper picker viewing a preview of it as well. 1273 */ 1274 public abstract Engine onCreateEngine(); 1275 1276 @Override 1277 protected void dump(FileDescriptor fd, PrintWriter out, String[] args) { 1278 out.print("State of wallpaper "); out.print(this); out.println(":"); 1279 for (int i=0; i<mActiveEngines.size(); i++) { 1280 Engine engine = mActiveEngines.get(i); 1281 out.print(" Engine "); out.print(engine); out.println(":"); 1282 engine.dump(" ", fd, out, args); 1283 } 1284 } 1285} 1286