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