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 17 package android.service.wallpaper; 18 19 import android.annotation.Nullable; 20 import android.annotation.SdkConstant; 21 import android.annotation.SdkConstant.SdkConstantType; 22 import android.annotation.SystemApi; 23 import android.app.Service; 24 import android.app.WallpaperColors; 25 import android.app.WallpaperInfo; 26 import android.app.WallpaperManager; 27 import android.compat.annotation.UnsupportedAppUsage; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.res.TypedArray; 31 import android.graphics.Bitmap; 32 import android.graphics.Canvas; 33 import android.graphics.PixelFormat; 34 import android.graphics.Rect; 35 import android.graphics.drawable.Drawable; 36 import android.hardware.display.DisplayManager; 37 import android.hardware.display.DisplayManager.DisplayListener; 38 import android.os.Build; 39 import android.os.Bundle; 40 import android.os.Handler; 41 import android.os.IBinder; 42 import android.os.Looper; 43 import android.os.Message; 44 import android.os.RemoteException; 45 import android.os.SystemClock; 46 import android.util.Log; 47 import android.util.MergedConfiguration; 48 import android.view.Display; 49 import android.view.DisplayCutout; 50 import android.view.DisplayInfo; 51 import android.view.Gravity; 52 import android.view.IWindowSession; 53 import android.view.InputChannel; 54 import android.view.InputDevice; 55 import android.view.InputEvent; 56 import android.view.InputEventReceiver; 57 import android.view.InsetsState; 58 import android.view.MotionEvent; 59 import android.view.SurfaceControl; 60 import android.view.SurfaceHolder; 61 import android.view.View; 62 import android.view.ViewGroup; 63 import android.view.WindowInsets; 64 import android.view.WindowManager; 65 import android.view.WindowManagerGlobal; 66 67 import com.android.internal.annotations.VisibleForTesting; 68 import com.android.internal.os.HandlerCaller; 69 import com.android.internal.view.BaseIWindow; 70 import com.android.internal.view.BaseSurfaceHolder; 71 72 import java.io.FileDescriptor; 73 import java.io.PrintWriter; 74 import java.util.ArrayList; 75 import java.util.concurrent.atomic.AtomicBoolean; 76 import java.util.function.Supplier; 77 78 /** 79 * A wallpaper service is responsible for showing a live wallpaper behind 80 * applications that would like to sit on top of it. This service object 81 * itself does very little -- its only purpose is to generate instances of 82 * {@link Engine} as needed. Implementing a wallpaper thus 83 * involves subclassing from this, subclassing an Engine implementation, 84 * and implementing {@link #onCreateEngine()} to return a new instance of 85 * your engine. 86 */ 87 public abstract class WallpaperService extends Service { 88 /** 89 * The {@link Intent} that must be declared as handled by the service. 90 * To be supported, the service must also require the 91 * {@link android.Manifest.permission#BIND_WALLPAPER} permission so 92 * that other applications can not abuse it. 93 */ 94 @SdkConstant(SdkConstantType.SERVICE_ACTION) 95 public static final String SERVICE_INTERFACE = 96 "android.service.wallpaper.WallpaperService"; 97 98 /** 99 * Name under which a WallpaperService component publishes information 100 * about itself. This meta-data must reference an XML resource containing 101 * a <code><{@link android.R.styleable#Wallpaper wallpaper}></code> 102 * tag. 103 */ 104 public static final String SERVICE_META_DATA = "android.service.wallpaper"; 105 106 static final String TAG = "WallpaperService"; 107 static final boolean DEBUG = false; 108 109 private static final int DO_ATTACH = 10; 110 private static final int DO_DETACH = 20; 111 private static final int DO_SET_DESIRED_SIZE = 30; 112 private static final int DO_SET_DISPLAY_PADDING = 40; 113 private static final int DO_IN_AMBIENT_MODE = 50; 114 115 private static final int MSG_UPDATE_SURFACE = 10000; 116 private static final int MSG_VISIBILITY_CHANGED = 10010; 117 private static final int MSG_WALLPAPER_OFFSETS = 10020; 118 private static final int MSG_WALLPAPER_COMMAND = 10025; 119 @UnsupportedAppUsage 120 private static final int MSG_WINDOW_RESIZED = 10030; 121 private static final int MSG_WINDOW_MOVED = 10035; 122 private static final int MSG_TOUCH_EVENT = 10040; 123 private static final int MSG_REQUEST_WALLPAPER_COLORS = 10050; 124 125 private static final int NOTIFY_COLORS_RATE_LIMIT_MS = 1000; 126 127 private final ArrayList<Engine> mActiveEngines 128 = new ArrayList<Engine>(); 129 130 static final class WallpaperCommand { 131 String action; 132 int x; 133 int y; 134 int z; 135 Bundle extras; 136 boolean sync; 137 } 138 139 /** 140 * The actual implementation of a wallpaper. A wallpaper service may 141 * have multiple instances running (for example as a real wallpaper 142 * and as a preview), each of which is represented by its own Engine 143 * instance. You must implement {@link WallpaperService#onCreateEngine()} 144 * to return your concrete Engine implementation. 145 */ 146 public class Engine { 147 IWallpaperEngineWrapper mIWallpaperEngine; 148 149 // Copies from mIWallpaperEngine. 150 HandlerCaller mCaller; 151 IWallpaperConnection mConnection; 152 IBinder mWindowToken; 153 154 boolean mInitializing = true; 155 boolean mVisible; 156 boolean mReportedVisible; 157 boolean mDestroyed; 158 159 // Current window state. 160 boolean mCreated; 161 boolean mSurfaceCreated; 162 boolean mIsCreating; 163 boolean mDrawingAllowed; 164 boolean mOffsetsChanged; 165 boolean mFixedSizeAllowed; 166 int mWidth; 167 int mHeight; 168 int mFormat; 169 int mType; 170 int mCurWidth; 171 int mCurHeight; 172 int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; 173 int mWindowPrivateFlags = 174 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS; 175 int mCurWindowFlags = mWindowFlags; 176 int mCurWindowPrivateFlags = mWindowPrivateFlags; 177 final Rect mVisibleInsets = new Rect(); 178 final Rect mWinFrame = new Rect(); 179 final Rect mOverscanInsets = new Rect(); 180 final Rect mContentInsets = new Rect(); 181 final Rect mStableInsets = new Rect(); 182 final Rect mOutsets = new Rect(); 183 final Rect mDispatchedOverscanInsets = new Rect(); 184 final Rect mDispatchedContentInsets = new Rect(); 185 final Rect mDispatchedStableInsets = new Rect(); 186 final Rect mDispatchedOutsets = new Rect(); 187 final Rect mFinalSystemInsets = new Rect(); 188 final Rect mFinalStableInsets = new Rect(); 189 final Rect mBackdropFrame = new Rect(); 190 final DisplayCutout.ParcelableWrapper mDisplayCutout = 191 new DisplayCutout.ParcelableWrapper(); 192 DisplayCutout mDispatchedDisplayCutout = DisplayCutout.NO_CUTOUT; 193 final InsetsState mInsetsState = new InsetsState(); 194 final MergedConfiguration mMergedConfiguration = new MergedConfiguration(); 195 196 final WindowManager.LayoutParams mLayout 197 = new WindowManager.LayoutParams(); 198 IWindowSession mSession; 199 InputChannel mInputChannel; 200 201 final Object mLock = new Object(); 202 boolean mOffsetMessageEnqueued; 203 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 204 float mPendingXOffset; 205 float mPendingYOffset; 206 float mPendingXOffsetStep; 207 float mPendingYOffsetStep; 208 boolean mPendingSync; 209 MotionEvent mPendingMove; 210 boolean mIsInAmbientMode; 211 212 // Needed for throttling onComputeColors. 213 private long mLastColorInvalidation; 214 private final Runnable mNotifyColorsChanged = this::notifyColorsChanged; 215 private final Supplier<Long> mClockFunction; 216 private final Handler mHandler; 217 218 private Display mDisplay; 219 private Context mDisplayContext; 220 private int mDisplayState; 221 222 SurfaceControl mSurfaceControl = new SurfaceControl(); 223 224 final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() { 225 { 226 mRequestedFormat = PixelFormat.RGBX_8888; 227 } 228 229 @Override 230 public boolean onAllowLockCanvas() { 231 return mDrawingAllowed; 232 } 233 234 @Override 235 public void onRelayoutContainer() { 236 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE); 237 mCaller.sendMessage(msg); 238 } 239 240 @Override 241 public void onUpdateSurface() { 242 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE); 243 mCaller.sendMessage(msg); 244 } 245 246 public boolean isCreating() { 247 return mIsCreating; 248 } 249 250 @Override 251 public void setFixedSize(int width, int height) { 252 if (!mFixedSizeAllowed) { 253 // Regular apps can't do this. It can only work for 254 // certain designs of window animations, so you can't 255 // rely on it. 256 throw new UnsupportedOperationException( 257 "Wallpapers currently only support sizing from layout"); 258 } 259 super.setFixedSize(width, height); 260 } 261 262 public void setKeepScreenOn(boolean screenOn) { 263 throw new UnsupportedOperationException( 264 "Wallpapers do not support keep screen on"); 265 } 266 267 private void prepareToDraw() { 268 if (mDisplayState == Display.STATE_DOZE 269 || mDisplayState == Display.STATE_DOZE_SUSPEND) { 270 try { 271 mSession.pokeDrawLock(mWindow); 272 } catch (RemoteException e) { 273 // System server died, can be ignored. 274 } 275 } 276 } 277 278 @Override 279 public Canvas lockCanvas() { 280 prepareToDraw(); 281 return super.lockCanvas(); 282 } 283 284 @Override 285 public Canvas lockCanvas(Rect dirty) { 286 prepareToDraw(); 287 return super.lockCanvas(dirty); 288 } 289 290 @Override 291 public Canvas lockHardwareCanvas() { 292 prepareToDraw(); 293 return super.lockHardwareCanvas(); 294 } 295 }; 296 297 final class WallpaperInputEventReceiver extends InputEventReceiver { WallpaperInputEventReceiver(InputChannel inputChannel, Looper looper)298 public WallpaperInputEventReceiver(InputChannel inputChannel, Looper looper) { 299 super(inputChannel, looper); 300 } 301 302 @Override onInputEvent(InputEvent event)303 public void onInputEvent(InputEvent event) { 304 boolean handled = false; 305 try { 306 if (event instanceof MotionEvent 307 && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { 308 MotionEvent dup = MotionEvent.obtainNoHistory((MotionEvent)event); 309 dispatchPointer(dup); 310 handled = true; 311 } 312 } finally { 313 finishInputEvent(event, handled); 314 } 315 } 316 } 317 WallpaperInputEventReceiver mInputEventReceiver; 318 319 final BaseIWindow mWindow = new BaseIWindow() { 320 @Override 321 public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, 322 Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw, 323 MergedConfiguration mergedConfiguration, Rect backDropRect, boolean forceLayout, 324 boolean alwaysConsumeSystemBars, int displayId, 325 DisplayCutout.ParcelableWrapper displayCutout) { 326 Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED, 327 reportDraw ? 1 : 0, outsets); 328 mCaller.sendMessage(msg); 329 } 330 331 @Override 332 public void moved(int newX, int newY) { 333 Message msg = mCaller.obtainMessageII(MSG_WINDOW_MOVED, newX, newY); 334 mCaller.sendMessage(msg); 335 } 336 337 @Override 338 public void dispatchAppVisibility(boolean visible) { 339 // We don't do this in preview mode; we'll let the preview 340 // activity tell us when to run. 341 if (!mIWallpaperEngine.mIsPreview) { 342 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 343 visible ? 1 : 0); 344 mCaller.sendMessage(msg); 345 } 346 } 347 348 @Override 349 public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, 350 boolean sync) { 351 synchronized (mLock) { 352 if (DEBUG) Log.v(TAG, "Dispatch wallpaper offsets: " + x + ", " + y); 353 mPendingXOffset = x; 354 mPendingYOffset = y; 355 mPendingXOffsetStep = xStep; 356 mPendingYOffsetStep = yStep; 357 if (sync) { 358 mPendingSync = true; 359 } 360 if (!mOffsetMessageEnqueued) { 361 mOffsetMessageEnqueued = true; 362 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS); 363 mCaller.sendMessage(msg); 364 } 365 } 366 } 367 368 @Override 369 public void dispatchWallpaperCommand(String action, int x, int y, 370 int z, Bundle extras, boolean sync) { 371 synchronized (mLock) { 372 if (DEBUG) Log.v(TAG, "Dispatch wallpaper command: " + x + ", " + y); 373 WallpaperCommand cmd = new WallpaperCommand(); 374 cmd.action = action; 375 cmd.x = x; 376 cmd.y = y; 377 cmd.z = z; 378 cmd.extras = extras; 379 cmd.sync = sync; 380 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_COMMAND); 381 msg.obj = cmd; 382 mCaller.sendMessage(msg); 383 } 384 } 385 }; 386 387 /** 388 * Default constructor 389 */ Engine()390 public Engine() { 391 this(SystemClock::elapsedRealtime, Handler.getMain()); 392 } 393 394 /** 395 * Constructor used for test purposes. 396 * 397 * @param clockFunction Supplies current times in millis. 398 * @param handler Used for posting/deferring asynchronous calls. 399 * @hide 400 */ 401 @VisibleForTesting Engine(Supplier<Long> clockFunction, Handler handler)402 public Engine(Supplier<Long> clockFunction, Handler handler) { 403 mClockFunction = clockFunction; 404 mHandler = handler; 405 } 406 407 /** 408 * Provides access to the surface in which this wallpaper is drawn. 409 */ getSurfaceHolder()410 public SurfaceHolder getSurfaceHolder() { 411 return mSurfaceHolder; 412 } 413 414 /** 415 * Convenience for {@link WallpaperManager#getDesiredMinimumWidth() 416 * WallpaperManager.getDesiredMinimumWidth()}, returning the width 417 * that the system would like this wallpaper to run in. 418 */ getDesiredMinimumWidth()419 public int getDesiredMinimumWidth() { 420 return mIWallpaperEngine.mReqWidth; 421 } 422 423 /** 424 * Convenience for {@link WallpaperManager#getDesiredMinimumHeight() 425 * WallpaperManager.getDesiredMinimumHeight()}, returning the height 426 * that the system would like this wallpaper to run in. 427 */ getDesiredMinimumHeight()428 public int getDesiredMinimumHeight() { 429 return mIWallpaperEngine.mReqHeight; 430 } 431 432 /** 433 * Return whether the wallpaper is currently visible to the user, 434 * this is the last value supplied to 435 * {@link #onVisibilityChanged(boolean)}. 436 */ isVisible()437 public boolean isVisible() { 438 return mReportedVisible; 439 } 440 441 /** 442 * Returns true if this engine is running in preview mode -- that is, 443 * it is being shown to the user before they select it as the actual 444 * wallpaper. 445 */ isPreview()446 public boolean isPreview() { 447 return mIWallpaperEngine.mIsPreview; 448 } 449 450 /** 451 * Returns true if this engine is running in ambient mode -- that is, 452 * it is being shown in low power mode, on always on display. 453 * @hide 454 */ 455 @SystemApi isInAmbientMode()456 public boolean isInAmbientMode() { 457 return mIsInAmbientMode; 458 } 459 460 /** 461 * Control whether this wallpaper will receive raw touch events 462 * from the window manager as the user interacts with the window 463 * that is currently displaying the wallpaper. By default they 464 * are turned off. If enabled, the events will be received in 465 * {@link #onTouchEvent(MotionEvent)}. 466 */ setTouchEventsEnabled(boolean enabled)467 public void setTouchEventsEnabled(boolean enabled) { 468 mWindowFlags = enabled 469 ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) 470 : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); 471 if (mCreated) { 472 updateSurface(false, false, false); 473 } 474 } 475 476 /** 477 * Control whether this wallpaper will receive notifications when the wallpaper 478 * has been scrolled. By default, wallpapers will receive notifications, although 479 * the default static image wallpapers do not. It is a performance optimization to 480 * set this to false. 481 * 482 * @param enabled whether the wallpaper wants to receive offset notifications 483 */ setOffsetNotificationsEnabled(boolean enabled)484 public void setOffsetNotificationsEnabled(boolean enabled) { 485 mWindowPrivateFlags = enabled 486 ? (mWindowPrivateFlags | 487 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS) 488 : (mWindowPrivateFlags & 489 ~WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS); 490 if (mCreated) { 491 updateSurface(false, false, false); 492 } 493 } 494 495 /** {@hide} */ 496 @UnsupportedAppUsage setFixedSizeAllowed(boolean allowed)497 public void setFixedSizeAllowed(boolean allowed) { 498 mFixedSizeAllowed = allowed; 499 } 500 501 /** 502 * Called once to initialize the engine. After returning, the 503 * engine's surface will be created by the framework. 504 */ onCreate(SurfaceHolder surfaceHolder)505 public void onCreate(SurfaceHolder surfaceHolder) { 506 } 507 508 /** 509 * Called right before the engine is going away. After this the 510 * surface will be destroyed and this Engine object is no longer 511 * valid. 512 */ onDestroy()513 public void onDestroy() { 514 } 515 516 /** 517 * Called to inform you of the wallpaper becoming visible or 518 * hidden. <em>It is very important that a wallpaper only use 519 * CPU while it is visible.</em>. 520 */ onVisibilityChanged(boolean visible)521 public void onVisibilityChanged(boolean visible) { 522 } 523 524 /** 525 * Called with the current insets that are in effect for the wallpaper. 526 * This gives you the part of the overall wallpaper surface that will 527 * generally be visible to the user (ignoring position offsets applied to it). 528 * 529 * @param insets Insets to apply. 530 */ onApplyWindowInsets(WindowInsets insets)531 public void onApplyWindowInsets(WindowInsets insets) { 532 } 533 534 /** 535 * Called as the user performs touch-screen interaction with the 536 * window that is currently showing this wallpaper. Note that the 537 * events you receive here are driven by the actual application the 538 * user is interacting with, so if it is slow you will get fewer 539 * move events. 540 */ onTouchEvent(MotionEvent event)541 public void onTouchEvent(MotionEvent event) { 542 } 543 544 /** 545 * Called to inform you of the wallpaper's offsets changing 546 * within its contain, corresponding to the container's 547 * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float) 548 * WallpaperManager.setWallpaperOffsets()}. 549 */ onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep, float yOffsetStep, int xPixelOffset, int yPixelOffset)550 public void onOffsetsChanged(float xOffset, float yOffset, 551 float xOffsetStep, float yOffsetStep, 552 int xPixelOffset, int yPixelOffset) { 553 } 554 555 /** 556 * Process a command that was sent to the wallpaper with 557 * {@link WallpaperManager#sendWallpaperCommand}. 558 * The default implementation does nothing, and always returns null 559 * as the result. 560 * 561 * @param action The name of the command to perform. This tells you 562 * what to do and how to interpret the rest of the arguments. 563 * @param x Generic integer parameter. 564 * @param y Generic integer parameter. 565 * @param z Generic integer parameter. 566 * @param extras Any additional parameters. 567 * @param resultRequested If true, the caller is requesting that 568 * a result, appropriate for the command, be returned back. 569 * @return If returning a result, create a Bundle and place the 570 * result data in to it. Otherwise return null. 571 */ onCommand(String action, int x, int y, int z, Bundle extras, boolean resultRequested)572 public Bundle onCommand(String action, int x, int y, int z, 573 Bundle extras, boolean resultRequested) { 574 return null; 575 } 576 577 /** 578 * Called when the device enters or exits ambient mode. 579 * 580 * @param inAmbientMode {@code true} if in ambient mode. 581 * @param animationDuration How long the transition animation to change the ambient state 582 * should run, in milliseconds. If 0 is passed as the argument 583 * here, the state should be switched immediately. 584 * 585 * @see #isInAmbientMode() 586 * @see WallpaperInfo#supportsAmbientMode() 587 * @hide 588 */ 589 @SystemApi onAmbientModeChanged(boolean inAmbientMode, long animationDuration)590 public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) { 591 } 592 593 /** 594 * Called when an application has changed the desired virtual size of 595 * the wallpaper. 596 */ onDesiredSizeChanged(int desiredWidth, int desiredHeight)597 public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) { 598 } 599 600 /** 601 * Convenience for {@link SurfaceHolder.Callback#surfaceChanged 602 * SurfaceHolder.Callback.surfaceChanged()}. 603 */ onSurfaceChanged(SurfaceHolder holder, int format, int width, int height)604 public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { 605 } 606 607 /** 608 * Convenience for {@link SurfaceHolder.Callback2#surfaceRedrawNeeded 609 * SurfaceHolder.Callback.surfaceRedrawNeeded()}. 610 */ onSurfaceRedrawNeeded(SurfaceHolder holder)611 public void onSurfaceRedrawNeeded(SurfaceHolder holder) { 612 } 613 614 /** 615 * Convenience for {@link SurfaceHolder.Callback#surfaceCreated 616 * SurfaceHolder.Callback.surfaceCreated()}. 617 */ onSurfaceCreated(SurfaceHolder holder)618 public void onSurfaceCreated(SurfaceHolder holder) { 619 } 620 621 /** 622 * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed 623 * SurfaceHolder.Callback.surfaceDestroyed()}. 624 */ onSurfaceDestroyed(SurfaceHolder holder)625 public void onSurfaceDestroyed(SurfaceHolder holder) { 626 } 627 628 /** 629 * Notifies the engine that wallpaper colors changed significantly. 630 * This will trigger a {@link #onComputeColors()} call. 631 */ notifyColorsChanged()632 public void notifyColorsChanged() { 633 final long now = mClockFunction.get(); 634 if (now - mLastColorInvalidation < NOTIFY_COLORS_RATE_LIMIT_MS) { 635 Log.w(TAG, "This call has been deferred. You should only call " 636 + "notifyColorsChanged() once every " 637 + (NOTIFY_COLORS_RATE_LIMIT_MS / 1000f) + " seconds."); 638 if (!mHandler.hasCallbacks(mNotifyColorsChanged)) { 639 mHandler.postDelayed(mNotifyColorsChanged, NOTIFY_COLORS_RATE_LIMIT_MS); 640 } 641 return; 642 } 643 mLastColorInvalidation = now; 644 mHandler.removeCallbacks(mNotifyColorsChanged); 645 646 try { 647 final WallpaperColors newColors = onComputeColors(); 648 if (mConnection != null) { 649 mConnection.onWallpaperColorsChanged(newColors, mDisplay.getDisplayId()); 650 } else { 651 Log.w(TAG, "Can't notify system because wallpaper connection " 652 + "was not established."); 653 } 654 } catch (RemoteException e) { 655 Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e); 656 } 657 } 658 659 /** 660 * Called by the system when it needs to know what colors the wallpaper is using. 661 * You might return null if no color information is available at the moment. 662 * In that case you might want to call {@link #notifyColorsChanged()} when 663 * color information becomes available. 664 * <p> 665 * The simplest way of creating a {@link android.app.WallpaperColors} object is by using 666 * {@link android.app.WallpaperColors#fromBitmap(Bitmap)} or 667 * {@link android.app.WallpaperColors#fromDrawable(Drawable)}, but you can also specify 668 * your main colors by constructing a {@link android.app.WallpaperColors} object manually. 669 * 670 * @return Wallpaper colors. 671 */ onComputeColors()672 public @Nullable WallpaperColors onComputeColors() { 673 return null; 674 } 675 676 /** 677 * Sets internal engine state. Only for testing. 678 * @param created {@code true} or {@code false}. 679 * @hide 680 */ 681 @VisibleForTesting setCreated(boolean created)682 public void setCreated(boolean created) { 683 mCreated = created; 684 } 685 dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args)686 protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) { 687 out.print(prefix); out.print("mInitializing="); out.print(mInitializing); 688 out.print(" mDestroyed="); out.println(mDestroyed); 689 out.print(prefix); out.print("mVisible="); out.print(mVisible); 690 out.print(" mReportedVisible="); out.println(mReportedVisible); 691 out.print(prefix); out.print("mDisplay="); out.println(mDisplay); 692 out.print(prefix); out.print("mCreated="); out.print(mCreated); 693 out.print(" mSurfaceCreated="); out.print(mSurfaceCreated); 694 out.print(" mIsCreating="); out.print(mIsCreating); 695 out.print(" mDrawingAllowed="); out.println(mDrawingAllowed); 696 out.print(prefix); out.print("mWidth="); out.print(mWidth); 697 out.print(" mCurWidth="); out.print(mCurWidth); 698 out.print(" mHeight="); out.print(mHeight); 699 out.print(" mCurHeight="); out.println(mCurHeight); 700 out.print(prefix); out.print("mType="); out.print(mType); 701 out.print(" mWindowFlags="); out.print(mWindowFlags); 702 out.print(" mCurWindowFlags="); out.println(mCurWindowFlags); 703 out.print(prefix); out.print("mWindowPrivateFlags="); out.print(mWindowPrivateFlags); 704 out.print(" mCurWindowPrivateFlags="); out.println(mCurWindowPrivateFlags); 705 out.print(prefix); out.print("mVisibleInsets="); 706 out.print(mVisibleInsets.toShortString()); 707 out.print(" mWinFrame="); out.print(mWinFrame.toShortString()); 708 out.print(" mContentInsets="); out.println(mContentInsets.toShortString()); 709 out.print(prefix); out.print("mConfiguration="); 710 out.println(mMergedConfiguration.getMergedConfiguration()); 711 out.print(prefix); out.print("mLayout="); out.println(mLayout); 712 synchronized (mLock) { 713 out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset); 714 out.print(" mPendingXOffset="); out.println(mPendingXOffset); 715 out.print(prefix); out.print("mPendingXOffsetStep="); 716 out.print(mPendingXOffsetStep); 717 out.print(" mPendingXOffsetStep="); out.println(mPendingXOffsetStep); 718 out.print(prefix); out.print("mOffsetMessageEnqueued="); 719 out.print(mOffsetMessageEnqueued); 720 out.print(" mPendingSync="); out.println(mPendingSync); 721 if (mPendingMove != null) { 722 out.print(prefix); out.print("mPendingMove="); out.println(mPendingMove); 723 } 724 } 725 } 726 dispatchPointer(MotionEvent event)727 private void dispatchPointer(MotionEvent event) { 728 if (event.isTouchEvent()) { 729 synchronized (mLock) { 730 if (event.getAction() == MotionEvent.ACTION_MOVE) { 731 mPendingMove = event; 732 } else { 733 mPendingMove = null; 734 } 735 } 736 Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event); 737 mCaller.sendMessage(msg); 738 } else { 739 event.recycle(); 740 } 741 } 742 updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded)743 void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) { 744 if (mDestroyed) { 745 Log.w(TAG, "Ignoring updateSurface: destroyed"); 746 } 747 748 boolean fixedSize = false; 749 int myWidth = mSurfaceHolder.getRequestedWidth(); 750 if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT; 751 else fixedSize = true; 752 int myHeight = mSurfaceHolder.getRequestedHeight(); 753 if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT; 754 else fixedSize = true; 755 756 final boolean creating = !mCreated; 757 final boolean surfaceCreating = !mSurfaceCreated; 758 final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat(); 759 boolean sizeChanged = mWidth != myWidth || mHeight != myHeight; 760 boolean insetsChanged = !mCreated; 761 final boolean typeChanged = mType != mSurfaceHolder.getRequestedType(); 762 final boolean flagsChanged = mCurWindowFlags != mWindowFlags || 763 mCurWindowPrivateFlags != mWindowPrivateFlags; 764 if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged 765 || typeChanged || flagsChanged || redrawNeeded 766 || !mIWallpaperEngine.mShownReported) { 767 768 if (DEBUG) Log.v(TAG, "Changes: creating=" + creating 769 + " format=" + formatChanged + " size=" + sizeChanged); 770 771 try { 772 mWidth = myWidth; 773 mHeight = myHeight; 774 mFormat = mSurfaceHolder.getRequestedFormat(); 775 mType = mSurfaceHolder.getRequestedType(); 776 777 mLayout.x = 0; 778 mLayout.y = 0; 779 780 if (!fixedSize) { 781 mLayout.width = myWidth; 782 mLayout.height = myHeight; 783 } else { 784 // Force the wallpaper to cover the screen in both dimensions 785 // only internal implementations like ImageWallpaper 786 DisplayInfo displayInfo = new DisplayInfo(); 787 mDisplay.getDisplayInfo(displayInfo); 788 final float layoutScale = Math.max( 789 (float) displayInfo.logicalHeight / (float) myHeight, 790 (float) displayInfo.logicalWidth / (float) myWidth); 791 mLayout.height = (int) (myHeight * layoutScale); 792 mLayout.width = (int) (myWidth * layoutScale); 793 mWindowFlags |= WindowManager.LayoutParams.FLAG_SCALED; 794 } 795 796 mLayout.format = mFormat; 797 798 mCurWindowFlags = mWindowFlags; 799 mLayout.flags = mWindowFlags 800 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 801 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR 802 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 803 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 804 mCurWindowPrivateFlags = mWindowPrivateFlags; 805 mLayout.privateFlags = mWindowPrivateFlags; 806 807 mLayout.memoryType = mType; 808 mLayout.token = mWindowToken; 809 810 if (!mCreated) { 811 // Retrieve watch round info 812 TypedArray windowStyle = obtainStyledAttributes( 813 com.android.internal.R.styleable.Window); 814 windowStyle.recycle(); 815 816 // Add window 817 mLayout.type = mIWallpaperEngine.mWindowType; 818 mLayout.gravity = Gravity.START|Gravity.TOP; 819 mLayout.setTitle(WallpaperService.this.getClass().getName()); 820 mLayout.windowAnimations = 821 com.android.internal.R.style.Animation_Wallpaper; 822 mInputChannel = new InputChannel(); 823 824 if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE, 825 mDisplay.getDisplayId(), mWinFrame, mContentInsets, mStableInsets, 826 mOutsets, mDisplayCutout, mInputChannel, 827 mInsetsState) < 0) { 828 Log.w(TAG, "Failed to add window while updating wallpaper surface."); 829 return; 830 } 831 mCreated = true; 832 833 mInputEventReceiver = new WallpaperInputEventReceiver( 834 mInputChannel, Looper.myLooper()); 835 } 836 837 mSurfaceHolder.mSurfaceLock.lock(); 838 mDrawingAllowed = true; 839 840 if (!fixedSize) { 841 mLayout.surfaceInsets.set(mIWallpaperEngine.mDisplayPadding); 842 mLayout.surfaceInsets.left += mOutsets.left; 843 mLayout.surfaceInsets.top += mOutsets.top; 844 mLayout.surfaceInsets.right += mOutsets.right; 845 mLayout.surfaceInsets.bottom += mOutsets.bottom; 846 } else { 847 mLayout.surfaceInsets.set(0, 0, 0, 0); 848 } 849 final int relayoutResult = mSession.relayout( 850 mWindow, mWindow.mSeq, mLayout, mWidth, mHeight, 851 View.VISIBLE, 0, -1, mWinFrame, mOverscanInsets, mContentInsets, 852 mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame, 853 mDisplayCutout, mMergedConfiguration, mSurfaceControl, 854 mInsetsState); 855 if (mSurfaceControl.isValid()) { 856 mSurfaceHolder.mSurface.copyFrom(mSurfaceControl); 857 mSurfaceControl.release(); 858 } 859 860 if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface 861 + ", frame=" + mWinFrame); 862 863 int w = mWinFrame.width(); 864 int h = mWinFrame.height(); 865 866 if (!fixedSize) { 867 final Rect padding = mIWallpaperEngine.mDisplayPadding; 868 w += padding.left + padding.right + mOutsets.left + mOutsets.right; 869 h += padding.top + padding.bottom + mOutsets.top + mOutsets.bottom; 870 mOverscanInsets.left += padding.left; 871 mOverscanInsets.top += padding.top; 872 mOverscanInsets.right += padding.right; 873 mOverscanInsets.bottom += padding.bottom; 874 mContentInsets.left += padding.left; 875 mContentInsets.top += padding.top; 876 mContentInsets.right += padding.right; 877 mContentInsets.bottom += padding.bottom; 878 mStableInsets.left += padding.left; 879 mStableInsets.top += padding.top; 880 mStableInsets.right += padding.right; 881 mStableInsets.bottom += padding.bottom; 882 mDisplayCutout.set(mDisplayCutout.get().inset(-padding.left, -padding.top, 883 -padding.right, -padding.bottom)); 884 } else { 885 w = myWidth; 886 h = myHeight; 887 } 888 889 if (mCurWidth != w) { 890 sizeChanged = true; 891 mCurWidth = w; 892 } 893 if (mCurHeight != h) { 894 sizeChanged = true; 895 mCurHeight = h; 896 } 897 898 if (DEBUG) { 899 Log.v(TAG, "Wallpaper size has changed: (" + mCurWidth + ", " + mCurHeight); 900 } 901 902 insetsChanged |= !mDispatchedOverscanInsets.equals(mOverscanInsets); 903 insetsChanged |= !mDispatchedContentInsets.equals(mContentInsets); 904 insetsChanged |= !mDispatchedStableInsets.equals(mStableInsets); 905 insetsChanged |= !mDispatchedOutsets.equals(mOutsets); 906 insetsChanged |= !mDispatchedDisplayCutout.equals(mDisplayCutout.get()); 907 908 mSurfaceHolder.setSurfaceFrameSize(w, h); 909 mSurfaceHolder.mSurfaceLock.unlock(); 910 911 if (!mSurfaceHolder.mSurface.isValid()) { 912 reportSurfaceDestroyed(); 913 if (DEBUG) Log.v(TAG, "Layout: Surface destroyed"); 914 return; 915 } 916 917 boolean didSurface = false; 918 919 try { 920 mSurfaceHolder.ungetCallbacks(); 921 922 if (surfaceCreating) { 923 mIsCreating = true; 924 didSurface = true; 925 if (DEBUG) Log.v(TAG, "onSurfaceCreated(" 926 + mSurfaceHolder + "): " + this); 927 onSurfaceCreated(mSurfaceHolder); 928 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 929 if (callbacks != null) { 930 for (SurfaceHolder.Callback c : callbacks) { 931 c.surfaceCreated(mSurfaceHolder); 932 } 933 } 934 } 935 936 redrawNeeded |= creating || (relayoutResult 937 & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0; 938 939 if (forceReport || creating || surfaceCreating 940 || formatChanged || sizeChanged) { 941 if (DEBUG) { 942 RuntimeException e = new RuntimeException(); 943 e.fillInStackTrace(); 944 Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating 945 + " formatChanged=" + formatChanged 946 + " sizeChanged=" + sizeChanged, e); 947 } 948 if (DEBUG) Log.v(TAG, "onSurfaceChanged(" 949 + mSurfaceHolder + ", " + mFormat 950 + ", " + mCurWidth + ", " + mCurHeight 951 + "): " + this); 952 didSurface = true; 953 onSurfaceChanged(mSurfaceHolder, mFormat, 954 mCurWidth, mCurHeight); 955 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 956 if (callbacks != null) { 957 for (SurfaceHolder.Callback c : callbacks) { 958 c.surfaceChanged(mSurfaceHolder, mFormat, 959 mCurWidth, mCurHeight); 960 } 961 } 962 } 963 964 if (insetsChanged) { 965 mDispatchedOverscanInsets.set(mOverscanInsets); 966 mDispatchedOverscanInsets.left += mOutsets.left; 967 mDispatchedOverscanInsets.top += mOutsets.top; 968 mDispatchedOverscanInsets.right += mOutsets.right; 969 mDispatchedOverscanInsets.bottom += mOutsets.bottom; 970 mDispatchedContentInsets.set(mContentInsets); 971 mDispatchedStableInsets.set(mStableInsets); 972 mDispatchedOutsets.set(mOutsets); 973 mDispatchedDisplayCutout = mDisplayCutout.get(); 974 mFinalSystemInsets.set(mDispatchedOverscanInsets); 975 mFinalStableInsets.set(mDispatchedStableInsets); 976 WindowInsets insets = new WindowInsets(mFinalSystemInsets, 977 mFinalStableInsets, 978 getResources().getConfiguration().isScreenRound(), false, 979 mDispatchedDisplayCutout); 980 if (DEBUG) { 981 Log.v(TAG, "dispatching insets=" + insets); 982 } 983 onApplyWindowInsets(insets); 984 } 985 986 if (redrawNeeded) { 987 onSurfaceRedrawNeeded(mSurfaceHolder); 988 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 989 if (callbacks != null) { 990 for (SurfaceHolder.Callback c : callbacks) { 991 if (c instanceof SurfaceHolder.Callback2) { 992 ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( 993 mSurfaceHolder); 994 } 995 } 996 } 997 } 998 999 if (didSurface && !mReportedVisible) { 1000 // This wallpaper is currently invisible, but its 1001 // surface has changed. At this point let's tell it 1002 // again that it is invisible in case the report about 1003 // the surface caused it to start running. We really 1004 // don't want wallpapers running when not visible. 1005 if (mIsCreating) { 1006 // Some wallpapers will ignore this call if they 1007 // had previously been told they were invisble, 1008 // so if we are creating a new surface then toggle 1009 // the state to get them to notice. 1010 if (DEBUG) Log.v(TAG, "onVisibilityChanged(true) at surface: " 1011 + this); 1012 onVisibilityChanged(true); 1013 } 1014 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false) at surface: " 1015 + this); 1016 onVisibilityChanged(false); 1017 } 1018 1019 } finally { 1020 mIsCreating = false; 1021 mSurfaceCreated = true; 1022 if (redrawNeeded) { 1023 mSession.finishDrawing(mWindow); 1024 } 1025 mIWallpaperEngine.reportShown(); 1026 } 1027 } catch (RemoteException ex) { 1028 } 1029 if (DEBUG) Log.v( 1030 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + 1031 " w=" + mLayout.width + " h=" + mLayout.height); 1032 } 1033 } 1034 attach(IWallpaperEngineWrapper wrapper)1035 void attach(IWallpaperEngineWrapper wrapper) { 1036 if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper); 1037 if (mDestroyed) { 1038 return; 1039 } 1040 1041 mIWallpaperEngine = wrapper; 1042 mCaller = wrapper.mCaller; 1043 mConnection = wrapper.mConnection; 1044 mWindowToken = wrapper.mWindowToken; 1045 mSurfaceHolder.setSizeFromLayout(); 1046 mInitializing = true; 1047 mSession = WindowManagerGlobal.getWindowSession(); 1048 1049 mWindow.setSession(mSession); 1050 1051 mLayout.packageName = getPackageName(); 1052 mIWallpaperEngine.mDisplayManager.registerDisplayListener(mDisplayListener, 1053 mCaller.getHandler()); 1054 mDisplay = mIWallpaperEngine.mDisplay; 1055 mDisplayContext = createDisplayContext(mDisplay); 1056 mDisplayState = mDisplay.getState(); 1057 1058 if (DEBUG) Log.v(TAG, "onCreate(): " + this); 1059 onCreate(mSurfaceHolder); 1060 1061 mInitializing = false; 1062 1063 mReportedVisible = false; 1064 updateSurface(false, false, false); 1065 } 1066 1067 /** 1068 * The {@link Context} with resources that match the current display the wallpaper is on. 1069 * For multiple display environment, multiple engines can be created to render on each 1070 * display, but these displays may have different densities. Use this context to get the 1071 * corresponding resources for currently display, avoiding the context of the service. 1072 * <p> 1073 * The display context will never be {@code null} after 1074 * {@link Engine#onCreate(SurfaceHolder)} has been called. 1075 * 1076 * @return A {@link Context} for current display. 1077 */ 1078 @Nullable getDisplayContext()1079 public Context getDisplayContext() { 1080 return mDisplayContext; 1081 } 1082 1083 /** 1084 * Executes life cycle event and updates internal ambient mode state based on 1085 * message sent from handler. 1086 * 1087 * @param inAmbientMode {@code true} if in ambient mode. 1088 * @param animationDuration For how long the transition will last, in ms. 1089 * @hide 1090 */ 1091 @VisibleForTesting doAmbientModeChanged(boolean inAmbientMode, long animationDuration)1092 public void doAmbientModeChanged(boolean inAmbientMode, long animationDuration) { 1093 if (!mDestroyed) { 1094 if (DEBUG) { 1095 Log.v(TAG, "onAmbientModeChanged(" + inAmbientMode + ", " 1096 + animationDuration + "): " + this); 1097 } 1098 mIsInAmbientMode = inAmbientMode; 1099 if (mCreated) { 1100 onAmbientModeChanged(inAmbientMode, animationDuration); 1101 } 1102 } 1103 } 1104 doDesiredSizeChanged(int desiredWidth, int desiredHeight)1105 void doDesiredSizeChanged(int desiredWidth, int desiredHeight) { 1106 if (!mDestroyed) { 1107 if (DEBUG) Log.v(TAG, "onDesiredSizeChanged(" 1108 + desiredWidth + "," + desiredHeight + "): " + this); 1109 mIWallpaperEngine.mReqWidth = desiredWidth; 1110 mIWallpaperEngine.mReqHeight = desiredHeight; 1111 onDesiredSizeChanged(desiredWidth, desiredHeight); 1112 doOffsetsChanged(true); 1113 } 1114 } 1115 doDisplayPaddingChanged(Rect padding)1116 void doDisplayPaddingChanged(Rect padding) { 1117 if (!mDestroyed) { 1118 if (DEBUG) Log.v(TAG, "onDisplayPaddingChanged(" + padding + "): " + this); 1119 if (!mIWallpaperEngine.mDisplayPadding.equals(padding)) { 1120 mIWallpaperEngine.mDisplayPadding.set(padding); 1121 updateSurface(true, false, false); 1122 } 1123 } 1124 } 1125 doVisibilityChanged(boolean visible)1126 void doVisibilityChanged(boolean visible) { 1127 if (!mDestroyed) { 1128 mVisible = visible; 1129 reportVisibility(); 1130 } 1131 } 1132 reportVisibility()1133 void reportVisibility() { 1134 if (!mDestroyed) { 1135 mDisplayState = mDisplay == null ? Display.STATE_UNKNOWN : mDisplay.getState(); 1136 boolean visible = mVisible && mDisplayState != Display.STATE_OFF; 1137 if (mReportedVisible != visible) { 1138 mReportedVisible = visible; 1139 if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible 1140 + "): " + this); 1141 if (visible) { 1142 // If becoming visible, in preview mode the surface 1143 // may have been destroyed so now we need to make 1144 // sure it is re-created. 1145 doOffsetsChanged(false); 1146 updateSurface(false, false, false); 1147 } 1148 onVisibilityChanged(visible); 1149 } 1150 } 1151 } 1152 doOffsetsChanged(boolean always)1153 void doOffsetsChanged(boolean always) { 1154 if (mDestroyed) { 1155 return; 1156 } 1157 1158 if (!always && !mOffsetsChanged) { 1159 return; 1160 } 1161 1162 float xOffset; 1163 float yOffset; 1164 float xOffsetStep; 1165 float yOffsetStep; 1166 boolean sync; 1167 synchronized (mLock) { 1168 xOffset = mPendingXOffset; 1169 yOffset = mPendingYOffset; 1170 xOffsetStep = mPendingXOffsetStep; 1171 yOffsetStep = mPendingYOffsetStep; 1172 sync = mPendingSync; 1173 mPendingSync = false; 1174 mOffsetMessageEnqueued = false; 1175 } 1176 1177 if (mSurfaceCreated) { 1178 if (mReportedVisible) { 1179 if (DEBUG) Log.v(TAG, "Offsets change in " + this 1180 + ": " + xOffset + "," + yOffset); 1181 final int availw = mIWallpaperEngine.mReqWidth-mCurWidth; 1182 final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0; 1183 final int availh = mIWallpaperEngine.mReqHeight-mCurHeight; 1184 final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0; 1185 onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels); 1186 } else { 1187 mOffsetsChanged = true; 1188 } 1189 } 1190 1191 if (sync) { 1192 try { 1193 if (DEBUG) Log.v(TAG, "Reporting offsets change complete"); 1194 mSession.wallpaperOffsetsComplete(mWindow.asBinder()); 1195 } catch (RemoteException e) { 1196 } 1197 } 1198 } 1199 doCommand(WallpaperCommand cmd)1200 void doCommand(WallpaperCommand cmd) { 1201 Bundle result; 1202 if (!mDestroyed) { 1203 result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z, 1204 cmd.extras, cmd.sync); 1205 } else { 1206 result = null; 1207 } 1208 if (cmd.sync) { 1209 try { 1210 if (DEBUG) Log.v(TAG, "Reporting command complete"); 1211 mSession.wallpaperCommandComplete(mWindow.asBinder(), result); 1212 } catch (RemoteException e) { 1213 } 1214 } 1215 } 1216 reportSurfaceDestroyed()1217 void reportSurfaceDestroyed() { 1218 if (mSurfaceCreated) { 1219 mSurfaceCreated = false; 1220 mSurfaceHolder.ungetCallbacks(); 1221 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 1222 if (callbacks != null) { 1223 for (SurfaceHolder.Callback c : callbacks) { 1224 c.surfaceDestroyed(mSurfaceHolder); 1225 } 1226 } 1227 if (DEBUG) Log.v(TAG, "onSurfaceDestroyed(" 1228 + mSurfaceHolder + "): " + this); 1229 onSurfaceDestroyed(mSurfaceHolder); 1230 } 1231 } 1232 detach()1233 void detach() { 1234 if (mDestroyed) { 1235 return; 1236 } 1237 1238 mDestroyed = true; 1239 1240 if (mIWallpaperEngine.mDisplayManager != null) { 1241 mIWallpaperEngine.mDisplayManager.unregisterDisplayListener(mDisplayListener); 1242 } 1243 1244 if (mVisible) { 1245 mVisible = false; 1246 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this); 1247 onVisibilityChanged(false); 1248 } 1249 1250 reportSurfaceDestroyed(); 1251 1252 if (DEBUG) Log.v(TAG, "onDestroy(): " + this); 1253 onDestroy(); 1254 1255 if (mCreated) { 1256 try { 1257 if (DEBUG) Log.v(TAG, "Removing window and destroying surface " 1258 + mSurfaceHolder.getSurface() + " of: " + this); 1259 1260 if (mInputEventReceiver != null) { 1261 mInputEventReceiver.dispose(); 1262 mInputEventReceiver = null; 1263 } 1264 1265 mSession.remove(mWindow); 1266 } catch (RemoteException e) { 1267 } 1268 mSurfaceHolder.mSurface.release(); 1269 mCreated = false; 1270 1271 // Dispose the input channel after removing the window so the Window Manager 1272 // doesn't interpret the input channel being closed as an abnormal termination. 1273 if (mInputChannel != null) { 1274 mInputChannel.dispose(); 1275 mInputChannel = null; 1276 } 1277 } 1278 } 1279 1280 private final DisplayListener mDisplayListener = new DisplayListener() { 1281 @Override 1282 public void onDisplayChanged(int displayId) { 1283 if (mDisplay.getDisplayId() == displayId) { 1284 reportVisibility(); 1285 } 1286 } 1287 1288 @Override 1289 public void onDisplayRemoved(int displayId) { 1290 } 1291 1292 @Override 1293 public void onDisplayAdded(int displayId) { 1294 } 1295 }; 1296 } 1297 1298 class IWallpaperEngineWrapper extends IWallpaperEngine.Stub 1299 implements HandlerCaller.Callback { 1300 private final HandlerCaller mCaller; 1301 1302 final IWallpaperConnection mConnection; 1303 final IBinder mWindowToken; 1304 final int mWindowType; 1305 final boolean mIsPreview; 1306 boolean mShownReported; 1307 int mReqWidth; 1308 int mReqHeight; 1309 final Rect mDisplayPadding = new Rect(); 1310 final int mDisplayId; 1311 final DisplayManager mDisplayManager; 1312 final Display mDisplay; 1313 private final AtomicBoolean mDetached = new AtomicBoolean(); 1314 1315 Engine mEngine; 1316 IWallpaperEngineWrapper(WallpaperService context, IWallpaperConnection conn, IBinder windowToken, int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding, int displayId)1317 IWallpaperEngineWrapper(WallpaperService context, 1318 IWallpaperConnection conn, IBinder windowToken, 1319 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding, 1320 int displayId) { 1321 mCaller = new HandlerCaller(context, context.getMainLooper(), this, true); 1322 mConnection = conn; 1323 mWindowToken = windowToken; 1324 mWindowType = windowType; 1325 mIsPreview = isPreview; 1326 mReqWidth = reqWidth; 1327 mReqHeight = reqHeight; 1328 mDisplayPadding.set(padding); 1329 mDisplayId = displayId; 1330 1331 // Create a display context before onCreateEngine. 1332 mDisplayManager = getSystemService(DisplayManager.class); 1333 mDisplay = mDisplayManager.getDisplay(mDisplayId); 1334 1335 if (mDisplay == null) { 1336 // Ignore this engine. 1337 throw new IllegalArgumentException("Cannot find display with id" + mDisplayId); 1338 } 1339 Message msg = mCaller.obtainMessage(DO_ATTACH); 1340 mCaller.sendMessage(msg); 1341 } 1342 setDesiredSize(int width, int height)1343 public void setDesiredSize(int width, int height) { 1344 Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height); 1345 mCaller.sendMessage(msg); 1346 } 1347 setDisplayPadding(Rect padding)1348 public void setDisplayPadding(Rect padding) { 1349 Message msg = mCaller.obtainMessageO(DO_SET_DISPLAY_PADDING, padding); 1350 mCaller.sendMessage(msg); 1351 } 1352 setVisibility(boolean visible)1353 public void setVisibility(boolean visible) { 1354 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 1355 visible ? 1 : 0); 1356 mCaller.sendMessage(msg); 1357 } 1358 1359 @Override setInAmbientMode(boolean inAmbientDisplay, long animationDuration)1360 public void setInAmbientMode(boolean inAmbientDisplay, long animationDuration) 1361 throws RemoteException { 1362 Message msg = mCaller.obtainMessageIO(DO_IN_AMBIENT_MODE, inAmbientDisplay ? 1 : 0, 1363 animationDuration); 1364 mCaller.sendMessage(msg); 1365 } 1366 dispatchPointer(MotionEvent event)1367 public void dispatchPointer(MotionEvent event) { 1368 if (mEngine != null) { 1369 mEngine.dispatchPointer(event); 1370 } else { 1371 event.recycle(); 1372 } 1373 } 1374 dispatchWallpaperCommand(String action, int x, int y, int z, Bundle extras)1375 public void dispatchWallpaperCommand(String action, int x, int y, 1376 int z, Bundle extras) { 1377 if (mEngine != null) { 1378 mEngine.mWindow.dispatchWallpaperCommand(action, x, y, z, extras, false); 1379 } 1380 } 1381 reportShown()1382 public void reportShown() { 1383 if (!mShownReported) { 1384 mShownReported = true; 1385 try { 1386 mConnection.engineShown(this); 1387 } catch (RemoteException e) { 1388 Log.w(TAG, "Wallpaper host disappeared", e); 1389 return; 1390 } 1391 } 1392 } 1393 requestWallpaperColors()1394 public void requestWallpaperColors() { 1395 Message msg = mCaller.obtainMessage(MSG_REQUEST_WALLPAPER_COLORS); 1396 mCaller.sendMessage(msg); 1397 } 1398 destroy()1399 public void destroy() { 1400 Message msg = mCaller.obtainMessage(DO_DETACH); 1401 mCaller.sendMessage(msg); 1402 } 1403 detach()1404 public void detach() { 1405 mDetached.set(true); 1406 } 1407 doDetachEngine()1408 private void doDetachEngine() { 1409 mActiveEngines.remove(mEngine); 1410 mEngine.detach(); 1411 } 1412 1413 @Override executeMessage(Message message)1414 public void executeMessage(Message message) { 1415 if (mDetached.get()) { 1416 if (mActiveEngines.contains(mEngine)) { 1417 doDetachEngine(); 1418 } 1419 return; 1420 } 1421 switch (message.what) { 1422 case DO_ATTACH: { 1423 try { 1424 mConnection.attachEngine(this, mDisplayId); 1425 } catch (RemoteException e) { 1426 Log.w(TAG, "Wallpaper host disappeared", e); 1427 return; 1428 } 1429 Engine engine = onCreateEngine(); 1430 mEngine = engine; 1431 mActiveEngines.add(engine); 1432 engine.attach(this); 1433 return; 1434 } 1435 case DO_DETACH: { 1436 doDetachEngine(); 1437 return; 1438 } 1439 case DO_SET_DESIRED_SIZE: { 1440 mEngine.doDesiredSizeChanged(message.arg1, message.arg2); 1441 return; 1442 } 1443 case DO_SET_DISPLAY_PADDING: { 1444 mEngine.doDisplayPaddingChanged((Rect) message.obj); 1445 return; 1446 } 1447 case DO_IN_AMBIENT_MODE: { 1448 mEngine.doAmbientModeChanged(message.arg1 != 0, (Long) message.obj); 1449 return; 1450 } 1451 case MSG_UPDATE_SURFACE: 1452 mEngine.updateSurface(true, false, false); 1453 break; 1454 case MSG_VISIBILITY_CHANGED: 1455 if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine 1456 + ": " + message.arg1); 1457 mEngine.doVisibilityChanged(message.arg1 != 0); 1458 break; 1459 case MSG_WALLPAPER_OFFSETS: { 1460 mEngine.doOffsetsChanged(true); 1461 } break; 1462 case MSG_WALLPAPER_COMMAND: { 1463 WallpaperCommand cmd = (WallpaperCommand)message.obj; 1464 mEngine.doCommand(cmd); 1465 } break; 1466 case MSG_WINDOW_RESIZED: { 1467 final boolean reportDraw = message.arg1 != 0; 1468 mEngine.mOutsets.set((Rect) message.obj); 1469 mEngine.updateSurface(true, false, reportDraw); 1470 mEngine.doOffsetsChanged(true); 1471 } break; 1472 case MSG_WINDOW_MOVED: { 1473 // Do nothing. What does it mean for a Wallpaper to move? 1474 } break; 1475 case MSG_TOUCH_EVENT: { 1476 boolean skip = false; 1477 MotionEvent ev = (MotionEvent)message.obj; 1478 if (ev.getAction() == MotionEvent.ACTION_MOVE) { 1479 synchronized (mEngine.mLock) { 1480 if (mEngine.mPendingMove == ev) { 1481 mEngine.mPendingMove = null; 1482 } else { 1483 // this is not the motion event we are looking for.... 1484 skip = true; 1485 } 1486 } 1487 } 1488 if (!skip) { 1489 if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev); 1490 mEngine.onTouchEvent(ev); 1491 } 1492 ev.recycle(); 1493 } break; 1494 case MSG_REQUEST_WALLPAPER_COLORS: { 1495 if (mConnection == null) { 1496 break; 1497 } 1498 try { 1499 mConnection.onWallpaperColorsChanged(mEngine.onComputeColors(), mDisplayId); 1500 } catch (RemoteException e) { 1501 // Connection went away, nothing to do in here. 1502 } 1503 } break; 1504 default : 1505 Log.w(TAG, "Unknown message type " + message.what); 1506 } 1507 } 1508 } 1509 1510 /** 1511 * Implements the internal {@link IWallpaperService} interface to convert 1512 * incoming calls to it back to calls on an {@link WallpaperService}. 1513 */ 1514 class IWallpaperServiceWrapper extends IWallpaperService.Stub { 1515 private final WallpaperService mTarget; 1516 private IWallpaperEngineWrapper mEngineWrapper; 1517 IWallpaperServiceWrapper(WallpaperService context)1518 public IWallpaperServiceWrapper(WallpaperService context) { 1519 mTarget = context; 1520 } 1521 1522 @Override attach(IWallpaperConnection conn, IBinder windowToken, int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding, int displayId)1523 public void attach(IWallpaperConnection conn, IBinder windowToken, 1524 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding, 1525 int displayId) { 1526 mEngineWrapper = new IWallpaperEngineWrapper(mTarget, conn, windowToken, 1527 windowType, isPreview, reqWidth, reqHeight, padding, displayId); 1528 } 1529 1530 @Override detach()1531 public void detach() { 1532 mEngineWrapper.detach(); 1533 } 1534 } 1535 1536 @Override onCreate()1537 public void onCreate() { 1538 super.onCreate(); 1539 } 1540 1541 @Override onDestroy()1542 public void onDestroy() { 1543 super.onDestroy(); 1544 for (int i=0; i<mActiveEngines.size(); i++) { 1545 mActiveEngines.get(i).detach(); 1546 } 1547 mActiveEngines.clear(); 1548 } 1549 1550 /** 1551 * Implement to return the implementation of the internal accessibility 1552 * service interface. Subclasses should not override. 1553 */ 1554 @Override onBind(Intent intent)1555 public final IBinder onBind(Intent intent) { 1556 return new IWallpaperServiceWrapper(this); 1557 } 1558 1559 /** 1560 * Must be implemented to return a new instance of the wallpaper's engine. 1561 * Note that multiple instances may be active at the same time, such as 1562 * when the wallpaper is currently set as the active wallpaper and the user 1563 * is in the wallpaper picker viewing a preview of it as well. 1564 */ onCreateEngine()1565 public abstract Engine onCreateEngine(); 1566 1567 @Override dump(FileDescriptor fd, PrintWriter out, String[] args)1568 protected void dump(FileDescriptor fd, PrintWriter out, String[] args) { 1569 out.print("State of wallpaper "); out.print(this); out.println(":"); 1570 for (int i=0; i<mActiveEngines.size(); i++) { 1571 Engine engine = mActiveEngines.get(i); 1572 out.print(" Engine "); out.print(engine); out.println(":"); 1573 engine.dump(" ", fd, out, args); 1574 } 1575 } 1576 } 1577