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(prefix); 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, mStableInsets, 668 mInputChannel) < 0) { 669 Log.w(TAG, "Failed to add window while updating wallpaper surface."); 670 return; 671 } 672 mCreated = true; 673 674 mInputEventReceiver = new WallpaperInputEventReceiver( 675 mInputChannel, Looper.myLooper()); 676 } 677 678 mSurfaceHolder.mSurfaceLock.lock(); 679 mDrawingAllowed = true; 680 681 if (!fixedSize) { 682 mLayout.surfaceInsets.set(mIWallpaperEngine.mDisplayPadding); 683 } else { 684 mLayout.surfaceInsets.set(0, 0, 0, 0); 685 } 686 final int relayoutResult = mSession.relayout( 687 mWindow, mWindow.mSeq, mLayout, mWidth, mHeight, 688 View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets, 689 mVisibleInsets, mStableInsets, mConfiguration, mSurfaceHolder.mSurface); 690 691 if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface 692 + ", frame=" + mWinFrame); 693 694 int w = mWinFrame.width(); 695 int h = mWinFrame.height(); 696 697 if (!fixedSize) { 698 final Rect padding = mIWallpaperEngine.mDisplayPadding; 699 w += padding.left + padding.right; 700 h += padding.top + padding.bottom; 701 mOverscanInsets.left += padding.left; 702 mOverscanInsets.top += padding.top; 703 mOverscanInsets.right += padding.right; 704 mOverscanInsets.bottom += padding.bottom; 705 mContentInsets.left += padding.left; 706 mContentInsets.top += padding.top; 707 mContentInsets.right += padding.right; 708 mContentInsets.bottom += padding.bottom; 709 mStableInsets.left += padding.left; 710 mStableInsets.top += padding.top; 711 mStableInsets.right += padding.right; 712 mStableInsets.bottom += padding.bottom; 713 } 714 715 if (mCurWidth != w) { 716 sizeChanged = true; 717 mCurWidth = w; 718 } 719 if (mCurHeight != h) { 720 sizeChanged = true; 721 mCurHeight = h; 722 } 723 724 insetsChanged |= !mDispatchedOverscanInsets.equals(mOverscanInsets); 725 insetsChanged |= !mDispatchedContentInsets.equals(mContentInsets); 726 insetsChanged |= !mDispatchedStableInsets.equals(mStableInsets); 727 728 mSurfaceHolder.setSurfaceFrameSize(w, h); 729 mSurfaceHolder.mSurfaceLock.unlock(); 730 731 if (!mSurfaceHolder.mSurface.isValid()) { 732 reportSurfaceDestroyed(); 733 if (DEBUG) Log.v(TAG, "Layout: Surface destroyed"); 734 return; 735 } 736 737 boolean didSurface = false; 738 739 try { 740 mSurfaceHolder.ungetCallbacks(); 741 742 if (surfaceCreating) { 743 mIsCreating = true; 744 didSurface = true; 745 if (DEBUG) Log.v(TAG, "onSurfaceCreated(" 746 + mSurfaceHolder + "): " + this); 747 onSurfaceCreated(mSurfaceHolder); 748 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 749 if (callbacks != null) { 750 for (SurfaceHolder.Callback c : callbacks) { 751 c.surfaceCreated(mSurfaceHolder); 752 } 753 } 754 } 755 756 redrawNeeded |= creating || (relayoutResult 757 & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0; 758 759 if (forceReport || creating || surfaceCreating 760 || formatChanged || sizeChanged) { 761 if (DEBUG) { 762 RuntimeException e = new RuntimeException(); 763 e.fillInStackTrace(); 764 Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating 765 + " formatChanged=" + formatChanged 766 + " sizeChanged=" + sizeChanged, e); 767 } 768 if (DEBUG) Log.v(TAG, "onSurfaceChanged(" 769 + mSurfaceHolder + ", " + mFormat 770 + ", " + mCurWidth + ", " + mCurHeight 771 + "): " + this); 772 didSurface = true; 773 onSurfaceChanged(mSurfaceHolder, mFormat, 774 mCurWidth, mCurHeight); 775 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 776 if (callbacks != null) { 777 for (SurfaceHolder.Callback c : callbacks) { 778 c.surfaceChanged(mSurfaceHolder, mFormat, 779 mCurWidth, mCurHeight); 780 } 781 } 782 } 783 784 if (insetsChanged) { 785 mDispatchedOverscanInsets.set(mOverscanInsets); 786 mDispatchedContentInsets.set(mContentInsets); 787 mDispatchedStableInsets.set(mStableInsets); 788 final boolean isRound = (mIsEmulator && mIsCircularEmulator) 789 || mWindowIsRound; 790 mFinalSystemInsets.set(mDispatchedOverscanInsets); 791 mFinalStableInsets.set(mDispatchedStableInsets); 792 if (mOutsetBottom != null) { 793 final DisplayMetrics metrics = getResources().getDisplayMetrics(); 794 mFinalSystemInsets.bottom = 795 ( (int) mOutsetBottom.getDimension(metrics) ) 796 + mIWallpaperEngine.mDisplayPadding.bottom; 797 } 798 WindowInsets insets = new WindowInsets(mFinalSystemInsets, 799 null, mFinalStableInsets, isRound); 800 onApplyWindowInsets(insets); 801 } 802 803 if (redrawNeeded) { 804 onSurfaceRedrawNeeded(mSurfaceHolder); 805 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 806 if (callbacks != null) { 807 for (SurfaceHolder.Callback c : callbacks) { 808 if (c instanceof SurfaceHolder.Callback2) { 809 ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( 810 mSurfaceHolder); 811 } 812 } 813 } 814 } 815 816 if (didSurface && !mReportedVisible) { 817 // This wallpaper is currently invisible, but its 818 // surface has changed. At this point let's tell it 819 // again that it is invisible in case the report about 820 // the surface caused it to start running. We really 821 // don't want wallpapers running when not visible. 822 if (mIsCreating) { 823 // Some wallpapers will ignore this call if they 824 // had previously been told they were invisble, 825 // so if we are creating a new surface then toggle 826 // the state to get them to notice. 827 if (DEBUG) Log.v(TAG, "onVisibilityChanged(true) at surface: " 828 + this); 829 onVisibilityChanged(true); 830 } 831 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false) at surface: " 832 + this); 833 onVisibilityChanged(false); 834 } 835 836 } finally { 837 mIsCreating = false; 838 mSurfaceCreated = true; 839 if (redrawNeeded) { 840 mSession.finishDrawing(mWindow); 841 } 842 mIWallpaperEngine.reportShown(); 843 } 844 } catch (RemoteException ex) { 845 } 846 if (DEBUG) Log.v( 847 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + 848 " w=" + mLayout.width + " h=" + mLayout.height); 849 } 850 } 851 852 void attach(IWallpaperEngineWrapper wrapper) { 853 if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper); 854 if (mDestroyed) { 855 return; 856 } 857 858 mIWallpaperEngine = wrapper; 859 mCaller = wrapper.mCaller; 860 mConnection = wrapper.mConnection; 861 mWindowToken = wrapper.mWindowToken; 862 mSurfaceHolder.setSizeFromLayout(); 863 mInitializing = true; 864 mSession = WindowManagerGlobal.getWindowSession(); 865 866 mWindow.setSession(mSession); 867 868 mDisplayManager = (DisplayManager)getSystemService(Context.DISPLAY_SERVICE); 869 mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler()); 870 mDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); 871 872 if (DEBUG) Log.v(TAG, "onCreate(): " + this); 873 onCreate(mSurfaceHolder); 874 875 mInitializing = false; 876 mReportedVisible = false; 877 updateSurface(false, false, false); 878 } 879 880 void doDesiredSizeChanged(int desiredWidth, int desiredHeight) { 881 if (!mDestroyed) { 882 if (DEBUG) Log.v(TAG, "onDesiredSizeChanged(" 883 + desiredWidth + "," + desiredHeight + "): " + this); 884 mIWallpaperEngine.mReqWidth = desiredWidth; 885 mIWallpaperEngine.mReqHeight = desiredHeight; 886 onDesiredSizeChanged(desiredWidth, desiredHeight); 887 doOffsetsChanged(true); 888 } 889 } 890 891 void doDisplayPaddingChanged(Rect padding) { 892 if (!mDestroyed) { 893 if (DEBUG) Log.v(TAG, "onDisplayPaddingChanged(" + padding + "): " + this); 894 if (!mIWallpaperEngine.mDisplayPadding.equals(padding)) { 895 mIWallpaperEngine.mDisplayPadding.set(padding); 896 updateSurface(true, false, false); 897 } 898 } 899 } 900 901 void doVisibilityChanged(boolean visible) { 902 if (!mDestroyed) { 903 mVisible = visible; 904 reportVisibility(); 905 } 906 } 907 908 void reportVisibility() { 909 if (!mDestroyed) { 910 boolean visible = mVisible 911 & mDisplay != null && mDisplay.getState() != Display.STATE_OFF; 912 if (mReportedVisible != visible) { 913 mReportedVisible = visible; 914 if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible 915 + "): " + this); 916 if (visible) { 917 // If becoming visible, in preview mode the surface 918 // may have been destroyed so now we need to make 919 // sure it is re-created. 920 doOffsetsChanged(false); 921 updateSurface(false, false, false); 922 } 923 onVisibilityChanged(visible); 924 } 925 } 926 } 927 928 void doOffsetsChanged(boolean always) { 929 if (mDestroyed) { 930 return; 931 } 932 933 if (!always && !mOffsetsChanged) { 934 return; 935 } 936 937 float xOffset; 938 float yOffset; 939 float xOffsetStep; 940 float yOffsetStep; 941 boolean sync; 942 synchronized (mLock) { 943 xOffset = mPendingXOffset; 944 yOffset = mPendingYOffset; 945 xOffsetStep = mPendingXOffsetStep; 946 yOffsetStep = mPendingYOffsetStep; 947 sync = mPendingSync; 948 mPendingSync = false; 949 mOffsetMessageEnqueued = false; 950 } 951 952 if (mSurfaceCreated) { 953 if (mReportedVisible) { 954 if (DEBUG) Log.v(TAG, "Offsets change in " + this 955 + ": " + xOffset + "," + yOffset); 956 final int availw = mIWallpaperEngine.mReqWidth-mCurWidth; 957 final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0; 958 final int availh = mIWallpaperEngine.mReqHeight-mCurHeight; 959 final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0; 960 onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels); 961 } else { 962 mOffsetsChanged = true; 963 } 964 } 965 966 if (sync) { 967 try { 968 if (DEBUG) Log.v(TAG, "Reporting offsets change complete"); 969 mSession.wallpaperOffsetsComplete(mWindow.asBinder()); 970 } catch (RemoteException e) { 971 } 972 } 973 } 974 975 void doCommand(WallpaperCommand cmd) { 976 Bundle result; 977 if (!mDestroyed) { 978 result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z, 979 cmd.extras, cmd.sync); 980 } else { 981 result = null; 982 } 983 if (cmd.sync) { 984 try { 985 if (DEBUG) Log.v(TAG, "Reporting command complete"); 986 mSession.wallpaperCommandComplete(mWindow.asBinder(), result); 987 } catch (RemoteException e) { 988 } 989 } 990 } 991 992 void reportSurfaceDestroyed() { 993 if (mSurfaceCreated) { 994 mSurfaceCreated = false; 995 mSurfaceHolder.ungetCallbacks(); 996 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 997 if (callbacks != null) { 998 for (SurfaceHolder.Callback c : callbacks) { 999 c.surfaceDestroyed(mSurfaceHolder); 1000 } 1001 } 1002 if (DEBUG) Log.v(TAG, "onSurfaceDestroyed(" 1003 + mSurfaceHolder + "): " + this); 1004 onSurfaceDestroyed(mSurfaceHolder); 1005 } 1006 } 1007 1008 void detach() { 1009 if (mDestroyed) { 1010 return; 1011 } 1012 1013 mDestroyed = true; 1014 1015 if (mDisplayManager != null) { 1016 mDisplayManager.unregisterDisplayListener(mDisplayListener); 1017 } 1018 1019 if (mVisible) { 1020 mVisible = false; 1021 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this); 1022 onVisibilityChanged(false); 1023 } 1024 1025 reportSurfaceDestroyed(); 1026 1027 if (DEBUG) Log.v(TAG, "onDestroy(): " + this); 1028 onDestroy(); 1029 1030 if (mCreated) { 1031 try { 1032 if (DEBUG) Log.v(TAG, "Removing window and destroying surface " 1033 + mSurfaceHolder.getSurface() + " of: " + this); 1034 1035 if (mInputEventReceiver != null) { 1036 mInputEventReceiver.dispose(); 1037 mInputEventReceiver = null; 1038 } 1039 1040 mSession.remove(mWindow); 1041 } catch (RemoteException e) { 1042 } 1043 mSurfaceHolder.mSurface.release(); 1044 mCreated = false; 1045 1046 // Dispose the input channel after removing the window so the Window Manager 1047 // doesn't interpret the input channel being closed as an abnormal termination. 1048 if (mInputChannel != null) { 1049 mInputChannel.dispose(); 1050 mInputChannel = null; 1051 } 1052 } 1053 } 1054 1055 private final DisplayListener mDisplayListener = new DisplayListener() { 1056 @Override 1057 public void onDisplayChanged(int displayId) { 1058 if (mDisplay.getDisplayId() == displayId) { 1059 reportVisibility(); 1060 } 1061 } 1062 1063 @Override 1064 public void onDisplayRemoved(int displayId) { 1065 } 1066 1067 @Override 1068 public void onDisplayAdded(int displayId) { 1069 } 1070 }; 1071 } 1072 1073 class IWallpaperEngineWrapper extends IWallpaperEngine.Stub 1074 implements HandlerCaller.Callback { 1075 private final HandlerCaller mCaller; 1076 1077 final IWallpaperConnection mConnection; 1078 final IBinder mWindowToken; 1079 final int mWindowType; 1080 final boolean mIsPreview; 1081 boolean mShownReported; 1082 int mReqWidth; 1083 int mReqHeight; 1084 final Rect mDisplayPadding = new Rect(); 1085 1086 Engine mEngine; 1087 1088 IWallpaperEngineWrapper(WallpaperService context, 1089 IWallpaperConnection conn, IBinder windowToken, 1090 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) { 1091 mCaller = new HandlerCaller(context, context.getMainLooper(), this, true); 1092 mConnection = conn; 1093 mWindowToken = windowToken; 1094 mWindowType = windowType; 1095 mIsPreview = isPreview; 1096 mReqWidth = reqWidth; 1097 mReqHeight = reqHeight; 1098 mDisplayPadding.set(padding); 1099 1100 Message msg = mCaller.obtainMessage(DO_ATTACH); 1101 mCaller.sendMessage(msg); 1102 } 1103 1104 public void setDesiredSize(int width, int height) { 1105 Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height); 1106 mCaller.sendMessage(msg); 1107 } 1108 1109 public void setDisplayPadding(Rect padding) { 1110 Message msg = mCaller.obtainMessageO(DO_SET_DISPLAY_PADDING, padding); 1111 mCaller.sendMessage(msg); 1112 } 1113 1114 public void setVisibility(boolean visible) { 1115 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 1116 visible ? 1 : 0); 1117 mCaller.sendMessage(msg); 1118 } 1119 1120 public void dispatchPointer(MotionEvent event) { 1121 if (mEngine != null) { 1122 mEngine.dispatchPointer(event); 1123 } else { 1124 event.recycle(); 1125 } 1126 } 1127 1128 public void dispatchWallpaperCommand(String action, int x, int y, 1129 int z, Bundle extras) { 1130 if (mEngine != null) { 1131 mEngine.mWindow.dispatchWallpaperCommand(action, x, y, z, extras, false); 1132 } 1133 } 1134 1135 public void reportShown() { 1136 if (!mShownReported) { 1137 mShownReported = true; 1138 try { 1139 mConnection.engineShown(this); 1140 } catch (RemoteException e) { 1141 Log.w(TAG, "Wallpaper host disappeared", e); 1142 return; 1143 } 1144 } 1145 } 1146 1147 public void destroy() { 1148 Message msg = mCaller.obtainMessage(DO_DETACH); 1149 mCaller.sendMessage(msg); 1150 } 1151 1152 public void executeMessage(Message message) { 1153 switch (message.what) { 1154 case DO_ATTACH: { 1155 try { 1156 mConnection.attachEngine(this); 1157 } catch (RemoteException e) { 1158 Log.w(TAG, "Wallpaper host disappeared", e); 1159 return; 1160 } 1161 Engine engine = onCreateEngine(); 1162 mEngine = engine; 1163 mActiveEngines.add(engine); 1164 engine.attach(this); 1165 return; 1166 } 1167 case DO_DETACH: { 1168 mActiveEngines.remove(mEngine); 1169 mEngine.detach(); 1170 return; 1171 } 1172 case DO_SET_DESIRED_SIZE: { 1173 mEngine.doDesiredSizeChanged(message.arg1, message.arg2); 1174 return; 1175 } 1176 case DO_SET_DISPLAY_PADDING: { 1177 mEngine.doDisplayPaddingChanged((Rect) message.obj); 1178 } 1179 case MSG_UPDATE_SURFACE: 1180 mEngine.updateSurface(true, false, false); 1181 break; 1182 case MSG_VISIBILITY_CHANGED: 1183 if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine 1184 + ": " + message.arg1); 1185 mEngine.doVisibilityChanged(message.arg1 != 0); 1186 break; 1187 case MSG_WALLPAPER_OFFSETS: { 1188 mEngine.doOffsetsChanged(true); 1189 } break; 1190 case MSG_WALLPAPER_COMMAND: { 1191 WallpaperCommand cmd = (WallpaperCommand)message.obj; 1192 mEngine.doCommand(cmd); 1193 } break; 1194 case MSG_WINDOW_RESIZED: { 1195 final boolean reportDraw = message.arg1 != 0; 1196 mEngine.updateSurface(true, false, reportDraw); 1197 mEngine.doOffsetsChanged(true); 1198 } break; 1199 case MSG_WINDOW_MOVED: { 1200 // Do nothing. What does it mean for a Wallpaper to move? 1201 } break; 1202 case MSG_TOUCH_EVENT: { 1203 boolean skip = false; 1204 MotionEvent ev = (MotionEvent)message.obj; 1205 if (ev.getAction() == MotionEvent.ACTION_MOVE) { 1206 synchronized (mEngine.mLock) { 1207 if (mEngine.mPendingMove == ev) { 1208 mEngine.mPendingMove = null; 1209 } else { 1210 // this is not the motion event we are looking for.... 1211 skip = true; 1212 } 1213 } 1214 } 1215 if (!skip) { 1216 if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev); 1217 mEngine.onTouchEvent(ev); 1218 } 1219 ev.recycle(); 1220 } break; 1221 default : 1222 Log.w(TAG, "Unknown message type " + message.what); 1223 } 1224 } 1225 } 1226 1227 /** 1228 * Implements the internal {@link IWallpaperService} interface to convert 1229 * incoming calls to it back to calls on an {@link WallpaperService}. 1230 */ 1231 class IWallpaperServiceWrapper extends IWallpaperService.Stub { 1232 private final WallpaperService mTarget; 1233 1234 public IWallpaperServiceWrapper(WallpaperService context) { 1235 mTarget = context; 1236 } 1237 1238 @Override 1239 public void attach(IWallpaperConnection conn, IBinder windowToken, 1240 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) { 1241 new IWallpaperEngineWrapper(mTarget, conn, windowToken, 1242 windowType, isPreview, reqWidth, reqHeight, padding); 1243 } 1244 } 1245 1246 @Override 1247 public void onCreate() { 1248 super.onCreate(); 1249 } 1250 1251 @Override 1252 public void onDestroy() { 1253 super.onDestroy(); 1254 for (int i=0; i<mActiveEngines.size(); i++) { 1255 mActiveEngines.get(i).detach(); 1256 } 1257 mActiveEngines.clear(); 1258 } 1259 1260 /** 1261 * Implement to return the implementation of the internal accessibility 1262 * service interface. Subclasses should not override. 1263 */ 1264 @Override 1265 public final IBinder onBind(Intent intent) { 1266 return new IWallpaperServiceWrapper(this); 1267 } 1268 1269 /** 1270 * Must be implemented to return a new instance of the wallpaper's engine. 1271 * Note that multiple instances may be active at the same time, such as 1272 * when the wallpaper is currently set as the active wallpaper and the user 1273 * is in the wallpaper picker viewing a preview of it as well. 1274 */ 1275 public abstract Engine onCreateEngine(); 1276 1277 @Override 1278 protected void dump(FileDescriptor fd, PrintWriter out, String[] args) { 1279 out.print("State of wallpaper "); out.print(this); out.println(":"); 1280 for (int i=0; i<mActiveEngines.size(); i++) { 1281 Engine engine = mActiveEngines.get(i); 1282 out.print(" Engine "); out.print(engine); out.println(":"); 1283 engine.dump(" ", fd, out, args); 1284 } 1285 } 1286} 1287