2 * Copyright (C) 2009 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.service.wallpaper;
19 import android.content.res.TypedArray;
20 import android.os.Build;
21 import android.os.SystemProperties;
22 import android.util.DisplayMetrics;
23 import android.util.TypedValue;
24 import android.view.ViewRootImpl;
25 import android.view.WindowInsets;
27 import com.android.internal.R;
28 import com.android.internal.os.HandlerCaller;
29 import com.android.internal.view.BaseIWindow;
30 import com.android.internal.view.BaseSurfaceHolder;
32 import android.annotation.SdkConstant;
33 import android.annotation.SdkConstant.SdkConstantType;
34 import android.app.Service;
35 import android.app.WallpaperManager;
36 import android.content.Context;
37 import android.content.Intent;
38 import android.content.res.Configuration;
39 import android.graphics.PixelFormat;
40 import android.graphics.Rect;
41 import android.hardware.display.DisplayManager;
42 import android.hardware.display.DisplayManager.DisplayListener;
43 import android.os.Bundle;
44 import android.os.IBinder;
45 import android.os.Looper;
46 import android.os.Message;
47 import android.os.RemoteException;
48 import android.util.Log;
49 import android.view.Display;
50 import android.view.Gravity;
51 import android.view.IWindowSession;
52 import android.view.InputChannel;
53 import android.view.InputDevice;
54 import android.view.InputEvent;
55 import android.view.InputEventReceiver;
56 import android.view.MotionEvent;
57 import android.view.SurfaceHolder;
58 import android.view.View;
59 import android.view.ViewGroup;
60 import android.view.WindowManager;
61 import android.view.WindowManagerGlobal;
63 import java.io.FileDescriptor;
64 import java.io.PrintWriter;
65 import java.util.ArrayList;
67 import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
70 * A wallpaper service is responsible for showing a live wallpaper behind
71 * applications that would like to sit on top of it. This service object
72 * itself does very little -- its only purpose is to generate instances of
73 * {@link Engine} as needed. Implementing a wallpaper thus
74 * involves subclassing from this, subclassing an Engine implementation,
75 * and implementing {@link #onCreateEngine()} to return a new instance of
78 public abstract class WallpaperService extends Service {
80 * The {@link Intent} that must be declared as handled by the service.
81 * To be supported, the service must also require the
82 * {@link android.Manifest.permission#BIND_WALLPAPER} permission so
83 * that other applications can not abuse it.
85 @SdkConstant(SdkConstantType.SERVICE_ACTION)
86 public static final String SERVICE_INTERFACE =
87 "android.service.wallpaper.WallpaperService";
90 * Name under which a WallpaperService component publishes information
91 * about itself. This meta-data must reference an XML resource containing
92 * a <code><{@link android.R.styleable#Wallpaper wallpaper}></code>
95 public static final String SERVICE_META_DATA = "android.service.wallpaper";
97 static final String TAG = "WallpaperService";
98 static final boolean DEBUG = false;
100 private static final int DO_ATTACH = 10;
101 private static final int DO_DETACH = 20;
102 private static final int DO_SET_DESIRED_SIZE = 30;
103 private static final int DO_SET_DISPLAY_PADDING = 40;
105 private static final int MSG_UPDATE_SURFACE = 10000;
106 private static final int MSG_VISIBILITY_CHANGED = 10010;
107 private static final int MSG_WALLPAPER_OFFSETS = 10020;
108 private static final int MSG_WALLPAPER_COMMAND = 10025;
109 private static final int MSG_WINDOW_RESIZED = 10030;
110 private static final int MSG_WINDOW_MOVED = 10035;
111 private static final int MSG_TOUCH_EVENT = 10040;
113 private final ArrayList<Engine> mActiveEngines
114 = new ArrayList<Engine>();
116 static final class WallpaperCommand {
126 * The actual implementation of a wallpaper. A wallpaper service may
127 * have multiple instances running (for example as a real wallpaper
128 * and as a preview), each of which is represented by its own Engine
129 * instance. You must implement {@link WallpaperService#onCreateEngine()}
130 * to return your concrete Engine implementation.
132 public class Engine {
133 IWallpaperEngineWrapper mIWallpaperEngine;
135 // Copies from mIWallpaperEngine.
136 HandlerCaller mCaller;
137 IWallpaperConnection mConnection;
138 IBinder mWindowToken;
140 boolean mInitializing = true;
142 boolean mReportedVisible;
145 // Current window state.
147 boolean mSurfaceCreated;
149 boolean mDrawingAllowed;
150 boolean mOffsetsChanged;
151 boolean mFixedSizeAllowed;
158 int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
159 int mWindowPrivateFlags =
160 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
161 int mCurWindowFlags = mWindowFlags;
162 int mCurWindowPrivateFlags = mWindowPrivateFlags;
163 TypedValue mOutsetBottom;
164 final Rect mVisibleInsets = new Rect();
165 final Rect mWinFrame = new Rect();
166 final Rect mOverscanInsets = new Rect();
167 final Rect mContentInsets = new Rect();
168 final Rect mStableInsets = new Rect();
169 final Rect mDispatchedOverscanInsets = new Rect();
170 final Rect mDispatchedContentInsets = new Rect();
171 final Rect mDispatchedStableInsets = new Rect();
172 final Rect mFinalSystemInsets = new Rect();
173 final Rect mFinalStableInsets = new Rect();
174 final Configuration mConfiguration = new Configuration();
176 private boolean mIsEmulator;
177 private boolean mIsCircularEmulator;
178 private boolean mWindowIsRound;
180 final WindowManager.LayoutParams mLayout
181 = new WindowManager.LayoutParams();
182 IWindowSession mSession;
183 InputChannel mInputChannel;
185 final Object mLock = new Object();
186 boolean mOffsetMessageEnqueued;
187 float mPendingXOffset;
188 float mPendingYOffset;
189 float mPendingXOffsetStep;
190 float mPendingYOffsetStep;
191 boolean mPendingSync;
192 MotionEvent mPendingMove;
194 DisplayManager mDisplayManager;
197 final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
199 mRequestedFormat = PixelFormat.RGBX_8888;
203 public boolean onAllowLockCanvas() {
204 return mDrawingAllowed;
208 public void onRelayoutContainer() {
209 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
210 mCaller.sendMessage(msg);
214 public void onUpdateSurface() {
215 Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
216 mCaller.sendMessage(msg);
219 public boolean isCreating() {
224 public void setFixedSize(int width, int height) {
225 if (!mFixedSizeAllowed) {
226 // Regular apps can't do this. It can only work for
227 // certain designs of window animations, so you can't
229 throw new UnsupportedOperationException(
230 "Wallpapers currently only support sizing from layout");
232 super.setFixedSize(width, height);
235 public void setKeepScreenOn(boolean screenOn) {
236 throw new UnsupportedOperationException(
237 "Wallpapers do not support keep screen on");
242 final class WallpaperInputEventReceiver extends InputEventReceiver {
243 public WallpaperInputEventReceiver(InputChannel inputChannel, Looper looper) {
244 super(inputChannel, looper);
248 public void onInputEvent(InputEvent event) {
249 boolean handled = false;
251 if (event instanceof MotionEvent
252 && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
253 MotionEvent dup = MotionEvent.obtainNoHistory((MotionEvent)event);
254 dispatchPointer(dup);
258 finishInputEvent(event, handled);
262 WallpaperInputEventReceiver mInputEventReceiver;
264 final BaseIWindow mWindow = new BaseIWindow() {
266 public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
267 Rect visibleInsets, Rect stableInsets, boolean reportDraw,
268 Configuration newConfig) {
269 Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED,
271 mCaller.sendMessage(msg);
275 public void moved(int newX, int newY) {
276 Message msg = mCaller.obtainMessageII(MSG_WINDOW_MOVED, newX, newY);
277 mCaller.sendMessage(msg);
281 public void dispatchAppVisibility(boolean visible) {
282 // We don't do this in preview mode; we'll let the preview
283 // activity tell us when to run.
284 if (!mIWallpaperEngine.mIsPreview) {
285 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
287 mCaller.sendMessage(msg);
292 public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
294 synchronized (mLock) {
295 if (DEBUG) Log.v(TAG, "Dispatch wallpaper offsets: " + x + ", " + y);
298 mPendingXOffsetStep = xStep;
299 mPendingYOffsetStep = yStep;
303 if (!mOffsetMessageEnqueued) {
304 mOffsetMessageEnqueued = true;
305 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS);
306 mCaller.sendMessage(msg);
312 public void dispatchWallpaperCommand(String action, int x, int y,
313 int z, Bundle extras, boolean sync) {
314 synchronized (mLock) {
315 if (DEBUG) Log.v(TAG, "Dispatch wallpaper command: " + x + ", " + y);
316 WallpaperCommand cmd = new WallpaperCommand();
323 Message msg = mCaller.obtainMessage(MSG_WALLPAPER_COMMAND);
325 mCaller.sendMessage(msg);
331 * Provides access to the surface in which this wallpaper is drawn.
333 public SurfaceHolder getSurfaceHolder() {
334 return mSurfaceHolder;
338 * Convenience for {@link WallpaperManager#getDesiredMinimumWidth()
339 * WallpaperManager.getDesiredMinimumWidth()}, returning the width
340 * that the system would like this wallpaper to run in.
342 public int getDesiredMinimumWidth() {
343 return mIWallpaperEngine.mReqWidth;
347 * Convenience for {@link WallpaperManager#getDesiredMinimumHeight()
348 * WallpaperManager.getDesiredMinimumHeight()}, returning the height
349 * that the system would like this wallpaper to run in.
351 public int getDesiredMinimumHeight() {
352 return mIWallpaperEngine.mReqHeight;
356 * Return whether the wallpaper is currently visible to the user,
357 * this is the last value supplied to
358 * {@link #onVisibilityChanged(boolean)}.
360 public boolean isVisible() {
361 return mReportedVisible;
365 * Returns true if this engine is running in preview mode -- that is,
366 * it is being shown to the user before they select it as the actual
369 public boolean isPreview() {
370 return mIWallpaperEngine.mIsPreview;
374 * Control whether this wallpaper will receive raw touch events
375 * from the window manager as the user interacts with the window
376 * that is currently displaying the wallpaper. By default they
377 * are turned off. If enabled, the events will be received in
378 * {@link #onTouchEvent(MotionEvent)}.
380 public void setTouchEventsEnabled(boolean enabled) {
381 mWindowFlags = enabled
382 ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
383 : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
385 updateSurface(false, false, false);
390 * Control whether this wallpaper will receive notifications when the wallpaper
391 * has been scrolled. By default, wallpapers will receive notifications, although
392 * the default static image wallpapers do not. It is a performance optimization to
395 * @param enabled whether the wallpaper wants to receive offset notifications
397 public void setOffsetNotificationsEnabled(boolean enabled) {
398 mWindowPrivateFlags = enabled
399 ? (mWindowPrivateFlags |
400 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS)
401 : (mWindowPrivateFlags &
402 ~WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS);
404 updateSurface(false, false, false);
409 public void setFixedSizeAllowed(boolean allowed) {
410 mFixedSizeAllowed = allowed;
414 * Called once to initialize the engine. After returning, the
415 * engine's surface will be created by the framework.
417 public void onCreate(SurfaceHolder surfaceHolder) {
421 * Called right before the engine is going away. After this the
422 * surface will be destroyed and this Engine object is no longer
425 public void onDestroy() {
429 * Called to inform you of the wallpaper becoming visible or
430 * hidden. <em>It is very important that a wallpaper only use
431 * CPU while it is visible.</em>.
433 public void onVisibilityChanged(boolean visible) {
437 * Called with the current insets that are in effect for the wallpaper.
438 * This gives you the part of the overall wallpaper surface that will
439 * generally be visible to the user (ignoring position offsets applied to it).
441 * @param insets Insets to apply.
443 public void onApplyWindowInsets(WindowInsets insets) {
447 * Called as the user performs touch-screen interaction with the
448 * window that is currently showing this wallpaper. Note that the
449 * events you receive here are driven by the actual application the
450 * user is interacting with, so if it is slow you will get fewer
453 public void onTouchEvent(MotionEvent event) {
457 * Called to inform you of the wallpaper's offsets changing
458 * within its contain, corresponding to the container's
459 * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float)
460 * WallpaperManager.setWallpaperOffsets()}.
462 public void onOffsetsChanged(float xOffset, float yOffset,
463 float xOffsetStep, float yOffsetStep,
464 int xPixelOffset, int yPixelOffset) {
468 * Process a command that was sent to the wallpaper with
469 * {@link WallpaperManager#sendWallpaperCommand}.
470 * The default implementation does nothing, and always returns null
473 * @param action The name of the command to perform. This tells you
474 * what to do and how to interpret the rest of the arguments.
475 * @param x Generic integer parameter.
476 * @param y Generic integer parameter.
477 * @param z Generic integer parameter.
478 * @param extras Any additional parameters.
479 * @param resultRequested If true, the caller is requesting that
480 * a result, appropriate for the command, be returned back.
481 * @return If returning a result, create a Bundle and place the
482 * result data in to it. Otherwise return null.
484 public Bundle onCommand(String action, int x, int y, int z,
485 Bundle extras, boolean resultRequested) {
490 * Called when an application has changed the desired virtual size of
493 public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) {
497 * Convenience for {@link SurfaceHolder.Callback#surfaceChanged
498 * SurfaceHolder.Callback.surfaceChanged()}.
500 public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
504 * Convenience for {@link SurfaceHolder.Callback2#surfaceRedrawNeeded
505 * SurfaceHolder.Callback.surfaceRedrawNeeded()}.
507 public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
511 * Convenience for {@link SurfaceHolder.Callback#surfaceCreated
512 * SurfaceHolder.Callback.surfaceCreated()}.
514 public void onSurfaceCreated(SurfaceHolder holder) {
518 * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed
519 * SurfaceHolder.Callback.surfaceDestroyed()}.
521 public void onSurfaceDestroyed(SurfaceHolder holder) {
524 protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
525 out.print(prefix); out.print("mInitializing="); out.print(mInitializing);
526 out.print(" mDestroyed="); out.println(mDestroyed);
527 out.print(prefix); out.print("mVisible="); out.print(mVisible);
528 out.print(" mReportedVisible="); out.println(mReportedVisible);
529 out.print(prefix); out.print("mDisplay="); out.println(mDisplay);
530 out.print(prefix); out.print("mCreated="); out.print(mCreated);
531 out.print(" mSurfaceCreated="); out.print(mSurfaceCreated);
532 out.print(" mIsCreating="); out.print(mIsCreating);
533 out.print(" mDrawingAllowed="); out.println(mDrawingAllowed);
534 out.print(prefix); out.print("mWidth="); out.print(mWidth);
535 out.print(" mCurWidth="); out.print(mCurWidth);
536 out.print(" mHeight="); out.print(mHeight);
537 out.print(" mCurHeight="); out.println(mCurHeight);
538 out.print(prefix); out.print("mType="); out.print(mType);
539 out.print(" mWindowFlags="); out.print(mWindowFlags);
540 out.print(" mCurWindowFlags="); out.println(mCurWindowFlags);
541 out.print(" mWindowPrivateFlags="); out.print(mWindowPrivateFlags);
542 out.print(" mCurWindowPrivateFlags="); out.println(mCurWindowPrivateFlags);
543 out.print(prefix); out.print("mVisibleInsets=");
544 out.print(mVisibleInsets.toShortString());
545 out.print(" mWinFrame="); out.print(mWinFrame.toShortString());
546 out.print(" mContentInsets="); out.println(mContentInsets.toShortString());
547 out.print(prefix); out.print("mConfiguration="); out.println(mConfiguration);
548 out.print(prefix); out.print("mLayout="); out.println(mLayout);
549 synchronized (mLock) {
550 out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset);
551 out.print(" mPendingXOffset="); out.println(mPendingXOffset);
552 out.print(prefix); out.print("mPendingXOffsetStep=");
553 out.print(mPendingXOffsetStep);
554 out.print(" mPendingXOffsetStep="); out.println(mPendingXOffsetStep);
555 out.print(prefix); out.print("mOffsetMessageEnqueued=");
556 out.print(mOffsetMessageEnqueued);
557 out.print(" mPendingSync="); out.println(mPendingSync);
558 if (mPendingMove != null) {
559 out.print(prefix); out.print("mPendingMove="); out.println(mPendingMove);
564 private void dispatchPointer(MotionEvent event) {
565 if (event.isTouchEvent()) {
566 synchronized (mLock) {
567 if (event.getAction() == MotionEvent.ACTION_MOVE) {
568 mPendingMove = event;
573 Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event);
574 mCaller.sendMessage(msg);
580 void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {
582 Log.w(TAG, "Ignoring updateSurface: destroyed");
585 boolean fixedSize = false;
586 int myWidth = mSurfaceHolder.getRequestedWidth();
587 if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT;
588 else fixedSize = true;
589 int myHeight = mSurfaceHolder.getRequestedHeight();
590 if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT;
591 else fixedSize = true;
593 final boolean creating = !mCreated;
594 final boolean surfaceCreating = !mSurfaceCreated;
595 final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat();
596 boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
597 boolean insetsChanged = !mCreated;
598 final boolean typeChanged = mType != mSurfaceHolder.getRequestedType();
599 final boolean flagsChanged = mCurWindowFlags != mWindowFlags ||
600 mCurWindowPrivateFlags != mWindowPrivateFlags;
601 if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged
602 || typeChanged || flagsChanged || redrawNeeded
603 || !mIWallpaperEngine.mShownReported) {
605 if (DEBUG) Log.v(TAG, "Changes: creating=" + creating
606 + " format=" + formatChanged + " size=" + sizeChanged);
611 mFormat = mSurfaceHolder.getRequestedFormat();
612 mType = mSurfaceHolder.getRequestedType();
616 mLayout.width = myWidth;
617 mLayout.height = myHeight;
619 mLayout.format = mFormat;
621 mCurWindowFlags = mWindowFlags;
622 mLayout.flags = mWindowFlags
623 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
624 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
625 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
627 mCurWindowPrivateFlags = mWindowPrivateFlags;
628 mLayout.privateFlags = mWindowPrivateFlags;
630 mLayout.memoryType = mType;
631 mLayout.token = mWindowToken;
634 // Retrieve watch round and outset info
635 final WindowManager windowService = (WindowManager)getSystemService(
636 Context.WINDOW_SERVICE);
637 TypedArray windowStyle = obtainStyledAttributes(
638 com.android.internal.R.styleable.Window);
639 final Display display = windowService.getDefaultDisplay();
640 final boolean shouldUseBottomOutset =
641 display.getDisplayId() == Display.DEFAULT_DISPLAY;
642 if (shouldUseBottomOutset && windowStyle.hasValue(
643 R.styleable.Window_windowOutsetBottom)) {
644 if (mOutsetBottom == null) mOutsetBottom = new TypedValue();
645 windowStyle.getValue(R.styleable.Window_windowOutsetBottom,
648 mOutsetBottom = null;
650 mWindowIsRound = getResources().getBoolean(
651 com.android.internal.R.bool.config_windowIsRound);
652 windowStyle.recycle();
655 mIsEmulator = Build.HARDWARE.contains("goldfish");
656 mIsCircularEmulator = SystemProperties.getBoolean(
657 ViewRootImpl.PROPERTY_EMULATOR_CIRCULAR, false);
660 mLayout.type = mIWallpaperEngine.mWindowType;
661 mLayout.gravity = Gravity.START|Gravity.TOP;
662 mLayout.setTitle(WallpaperService.this.getClass().getName());
663 mLayout.windowAnimations =
664 com.android.internal.R.style.Animation_Wallpaper;
665 mInputChannel = new InputChannel();
666 if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
667 Display.DEFAULT_DISPLAY, mContentInsets, mStableInsets,
668 mInputChannel) < 0) {
669 Log.w(TAG, "Failed to add window while updating wallpaper surface.");
674 mInputEventReceiver = new WallpaperInputEventReceiver(
675 mInputChannel, Looper.myLooper());
678 mSurfaceHolder.mSurfaceLock.lock();
679 mDrawingAllowed = true;
682 mLayout.surfaceInsets.set(mIWallpaperEngine.mDisplayPadding);
684 mLayout.surfaceInsets.set(0, 0, 0, 0);
686 final int relayoutResult = mSession.relayout(
687 mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
688 View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets,
689 mVisibleInsets, mStableInsets, mConfiguration, mSurfaceHolder.mSurface);
691 if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
692 + ", frame=" + mWinFrame);
694 int w = mWinFrame.width();
695 int h = mWinFrame.height();
698 final Rect padding = mIWallpaperEngine.mDisplayPadding;
699 w += padding.left + padding.right;
700 h += padding.top + padding.bottom;
701 mOverscanInsets.left += padding.left;
702 mOverscanInsets.top += padding.top;
703 mOverscanInsets.right += padding.right;
704 mOverscanInsets.bottom += padding.bottom;
705 mContentInsets.left += padding.left;
706 mContentInsets.top += padding.top;
707 mContentInsets.right += padding.right;
708 mContentInsets.bottom += padding.bottom;
709 mStableInsets.left += padding.left;
710 mStableInsets.top += padding.top;
711 mStableInsets.right += padding.right;
712 mStableInsets.bottom += padding.bottom;
715 if (mCurWidth != w) {
719 if (mCurHeight != h) {
724 insetsChanged |= !mDispatchedOverscanInsets.equals(mOverscanInsets);
725 insetsChanged |= !mDispatchedContentInsets.equals(mContentInsets);
726 insetsChanged |= !mDispatchedStableInsets.equals(mStableInsets);
728 mSurfaceHolder.setSurfaceFrameSize(w, h);
729 mSurfaceHolder.mSurfaceLock.unlock();
731 if (!mSurfaceHolder.mSurface.isValid()) {
732 reportSurfaceDestroyed();
733 if (DEBUG) Log.v(TAG, "Layout: Surface destroyed");
737 boolean didSurface = false;
740 mSurfaceHolder.ungetCallbacks();
742 if (surfaceCreating) {
745 if (DEBUG) Log.v(TAG, "onSurfaceCreated("
746 + mSurfaceHolder + "): " + this);
747 onSurfaceCreated(mSurfaceHolder);
748 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
749 if (callbacks != null) {
750 for (SurfaceHolder.Callback c : callbacks) {
751 c.surfaceCreated(mSurfaceHolder);
756 redrawNeeded |= creating || (relayoutResult
757 & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;
759 if (forceReport || creating || surfaceCreating
760 || formatChanged || sizeChanged) {
762 RuntimeException e = new RuntimeException();
763 e.fillInStackTrace();
764 Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating
765 + " formatChanged=" + formatChanged
766 + " sizeChanged=" + sizeChanged, e);
768 if (DEBUG) Log.v(TAG, "onSurfaceChanged("
769 + mSurfaceHolder + ", " + mFormat
770 + ", " + mCurWidth + ", " + mCurHeight
773 onSurfaceChanged(mSurfaceHolder, mFormat,
774 mCurWidth, mCurHeight);
775 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
776 if (callbacks != null) {
777 for (SurfaceHolder.Callback c : callbacks) {
778 c.surfaceChanged(mSurfaceHolder, mFormat,
779 mCurWidth, mCurHeight);
785 mDispatchedOverscanInsets.set(mOverscanInsets);
786 mDispatchedContentInsets.set(mContentInsets);
787 mDispatchedStableInsets.set(mStableInsets);
788 final boolean isRound = (mIsEmulator && mIsCircularEmulator)
790 mFinalSystemInsets.set(mDispatchedOverscanInsets);
791 mFinalStableInsets.set(mDispatchedStableInsets);
792 if (mOutsetBottom != null) {
793 final DisplayMetrics metrics = getResources().getDisplayMetrics();
794 mFinalSystemInsets.bottom =
795 ( (int) mOutsetBottom.getDimension(metrics) )
796 + mIWallpaperEngine.mDisplayPadding.bottom;
798 WindowInsets insets = new WindowInsets(mFinalSystemInsets,
799 null, mFinalStableInsets, isRound);
800 onApplyWindowInsets(insets);
804 onSurfaceRedrawNeeded(mSurfaceHolder);
805 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
806 if (callbacks != null) {
807 for (SurfaceHolder.Callback c : callbacks) {
808 if (c instanceof SurfaceHolder.Callback2) {
809 ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
816 if (didSurface && !mReportedVisible) {
817 // This wallpaper is currently invisible, but its
818 // surface has changed. At this point let's tell it
819 // again that it is invisible in case the report about
820 // the surface caused it to start running. We really
821 // don't want wallpapers running when not visible.
823 // Some wallpapers will ignore this call if they
824 // had previously been told they were invisble,
825 // so if we are creating a new surface then toggle
826 // the state to get them to notice.
827 if (DEBUG) Log.v(TAG, "onVisibilityChanged(true) at surface: "
829 onVisibilityChanged(true);
831 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false) at surface: "
833 onVisibilityChanged(false);
838 mSurfaceCreated = true;
840 mSession.finishDrawing(mWindow);
842 mIWallpaperEngine.reportShown();
844 } catch (RemoteException ex) {
847 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
848 " w=" + mLayout.width + " h=" + mLayout.height);
852 void attach(IWallpaperEngineWrapper wrapper) {
853 if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper);
858 mIWallpaperEngine = wrapper;
859 mCaller = wrapper.mCaller;
860 mConnection = wrapper.mConnection;
861 mWindowToken = wrapper.mWindowToken;
862 mSurfaceHolder.setSizeFromLayout();
863 mInitializing = true;
864 mSession = WindowManagerGlobal.getWindowSession();
866 mWindow.setSession(mSession);
868 mDisplayManager = (DisplayManager)getSystemService(Context.DISPLAY_SERVICE);
869 mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler());
870 mDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
872 if (DEBUG) Log.v(TAG, "onCreate(): " + this);
873 onCreate(mSurfaceHolder);
875 mInitializing = false;
876 mReportedVisible = false;
877 updateSurface(false, false, false);
880 void doDesiredSizeChanged(int desiredWidth, int desiredHeight) {
882 if (DEBUG) Log.v(TAG, "onDesiredSizeChanged("
883 + desiredWidth + "," + desiredHeight + "): " + this);
884 mIWallpaperEngine.mReqWidth = desiredWidth;
885 mIWallpaperEngine.mReqHeight = desiredHeight;
886 onDesiredSizeChanged(desiredWidth, desiredHeight);
887 doOffsetsChanged(true);
891 void doDisplayPaddingChanged(Rect padding) {
893 if (DEBUG) Log.v(TAG, "onDisplayPaddingChanged(" + padding + "): " + this);
894 if (!mIWallpaperEngine.mDisplayPadding.equals(padding)) {
895 mIWallpaperEngine.mDisplayPadding.set(padding);
896 updateSurface(true, false, false);
901 void doVisibilityChanged(boolean visible) {
908 void reportVisibility() {
910 boolean visible = mVisible
911 & mDisplay != null && mDisplay.getState() != Display.STATE_OFF;
912 if (mReportedVisible != visible) {
913 mReportedVisible = visible;
914 if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible
917 // If becoming visible, in preview mode the surface
918 // may have been destroyed so now we need to make
919 // sure it is re-created.
920 doOffsetsChanged(false);
921 updateSurface(false, false, false);
923 onVisibilityChanged(visible);
928 void doOffsetsChanged(boolean always) {
933 if (!always && !mOffsetsChanged) {
942 synchronized (mLock) {
943 xOffset = mPendingXOffset;
944 yOffset = mPendingYOffset;
945 xOffsetStep = mPendingXOffsetStep;
946 yOffsetStep = mPendingYOffsetStep;
948 mPendingSync = false;
949 mOffsetMessageEnqueued = false;
952 if (mSurfaceCreated) {
953 if (mReportedVisible) {
954 if (DEBUG) Log.v(TAG, "Offsets change in " + this
955 + ": " + xOffset + "," + yOffset);
956 final int availw = mIWallpaperEngine.mReqWidth-mCurWidth;
957 final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0;
958 final int availh = mIWallpaperEngine.mReqHeight-mCurHeight;
959 final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0;
960 onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels);
962 mOffsetsChanged = true;
968 if (DEBUG) Log.v(TAG, "Reporting offsets change complete");
969 mSession.wallpaperOffsetsComplete(mWindow.asBinder());
970 } catch (RemoteException e) {
975 void doCommand(WallpaperCommand cmd) {
978 result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z,
979 cmd.extras, cmd.sync);
985 if (DEBUG) Log.v(TAG, "Reporting command complete");
986 mSession.wallpaperCommandComplete(mWindow.asBinder(), result);
987 } catch (RemoteException e) {
992 void reportSurfaceDestroyed() {
993 if (mSurfaceCreated) {
994 mSurfaceCreated = false;
995 mSurfaceHolder.ungetCallbacks();
996 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
997 if (callbacks != null) {
998 for (SurfaceHolder.Callback c : callbacks) {
999 c.surfaceDestroyed(mSurfaceHolder);
1002 if (DEBUG) Log.v(TAG, "onSurfaceDestroyed("
1003 + mSurfaceHolder + "): " + this);
1004 onSurfaceDestroyed(mSurfaceHolder);
1015 if (mDisplayManager != null) {
1016 mDisplayManager.unregisterDisplayListener(mDisplayListener);
1021 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this);
1022 onVisibilityChanged(false);
1025 reportSurfaceDestroyed();
1027 if (DEBUG) Log.v(TAG, "onDestroy(): " + this);
1032 if (DEBUG) Log.v(TAG, "Removing window and destroying surface "
1033 + mSurfaceHolder.getSurface() + " of: " + this);
1035 if (mInputEventReceiver != null) {
1036 mInputEventReceiver.dispose();
1037 mInputEventReceiver = null;
1040 mSession.remove(mWindow);
1041 } catch (RemoteException e) {
1043 mSurfaceHolder.mSurface.release();
1046 // Dispose the input channel after removing the window so the Window Manager
1047 // doesn't interpret the input channel being closed as an abnormal termination.
1048 if (mInputChannel != null) {
1049 mInputChannel.dispose();
1050 mInputChannel = null;
1055 private final DisplayListener mDisplayListener = new DisplayListener() {
1057 public void onDisplayChanged(int displayId) {
1058 if (mDisplay.getDisplayId() == displayId) {
1064 public void onDisplayRemoved(int displayId) {
1068 public void onDisplayAdded(int displayId) {
1073 class IWallpaperEngineWrapper extends IWallpaperEngine.Stub
1074 implements HandlerCaller.Callback {
1075 private final HandlerCaller mCaller;
1077 final IWallpaperConnection mConnection;
1078 final IBinder mWindowToken;
1079 final int mWindowType;
1080 final boolean mIsPreview;
1081 boolean mShownReported;
1084 final Rect mDisplayPadding = new Rect();
1088 IWallpaperEngineWrapper(WallpaperService context,
1089 IWallpaperConnection conn, IBinder windowToken,
1090 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) {
1091 mCaller = new HandlerCaller(context, context.getMainLooper(), this, true);
1093 mWindowToken = windowToken;
1094 mWindowType = windowType;
1095 mIsPreview = isPreview;
1096 mReqWidth = reqWidth;
1097 mReqHeight = reqHeight;
1098 mDisplayPadding.set(padding);
1100 Message msg = mCaller.obtainMessage(DO_ATTACH);
1101 mCaller.sendMessage(msg);
1104 public void setDesiredSize(int width, int height) {
1105 Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height);
1106 mCaller.sendMessage(msg);
1109 public void setDisplayPadding(Rect padding) {
1110 Message msg = mCaller.obtainMessageO(DO_SET_DISPLAY_PADDING, padding);
1111 mCaller.sendMessage(msg);
1114 public void setVisibility(boolean visible) {
1115 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
1117 mCaller.sendMessage(msg);
1120 public void dispatchPointer(MotionEvent event) {
1121 if (mEngine != null) {
1122 mEngine.dispatchPointer(event);
1128 public void dispatchWallpaperCommand(String action, int x, int y,
1129 int z, Bundle extras) {
1130 if (mEngine != null) {
1131 mEngine.mWindow.dispatchWallpaperCommand(action, x, y, z, extras, false);
1135 public void reportShown() {
1136 if (!mShownReported) {
1137 mShownReported = true;
1139 mConnection.engineShown(this);
1140 } catch (RemoteException e) {
1141 Log.w(TAG, "Wallpaper host disappeared", e);
1147 public void destroy() {
1148 Message msg = mCaller.obtainMessage(DO_DETACH);
1149 mCaller.sendMessage(msg);
1152 public void executeMessage(Message message) {
1153 switch (message.what) {
1156 mConnection.attachEngine(this);
1157 } catch (RemoteException e) {
1158 Log.w(TAG, "Wallpaper host disappeared", e);
1161 Engine engine = onCreateEngine();
1163 mActiveEngines.add(engine);
1164 engine.attach(this);
1168 mActiveEngines.remove(mEngine);
1172 case DO_SET_DESIRED_SIZE: {
1173 mEngine.doDesiredSizeChanged(message.arg1, message.arg2);
1176 case DO_SET_DISPLAY_PADDING: {
1177 mEngine.doDisplayPaddingChanged((Rect) message.obj);
1179 case MSG_UPDATE_SURFACE:
1180 mEngine.updateSurface(true, false, false);
1182 case MSG_VISIBILITY_CHANGED:
1183 if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine
1184 + ": " + message.arg1);
1185 mEngine.doVisibilityChanged(message.arg1 != 0);
1187 case MSG_WALLPAPER_OFFSETS: {
1188 mEngine.doOffsetsChanged(true);
1190 case MSG_WALLPAPER_COMMAND: {
1191 WallpaperCommand cmd = (WallpaperCommand)message.obj;
1192 mEngine.doCommand(cmd);
1194 case MSG_WINDOW_RESIZED: {
1195 final boolean reportDraw = message.arg1 != 0;
1196 mEngine.updateSurface(true, false, reportDraw);
1197 mEngine.doOffsetsChanged(true);
1199 case MSG_WINDOW_MOVED: {
1200 // Do nothing. What does it mean for a Wallpaper to move?
1202 case MSG_TOUCH_EVENT: {
1203 boolean skip = false;
1204 MotionEvent ev = (MotionEvent)message.obj;
1205 if (ev.getAction() == MotionEvent.ACTION_MOVE) {
1206 synchronized (mEngine.mLock) {
1207 if (mEngine.mPendingMove == ev) {
1208 mEngine.mPendingMove = null;
1210 // this is not the motion event we are looking for....
1216 if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev);
1217 mEngine.onTouchEvent(ev);
1222 Log.w(TAG, "Unknown message type " + message.what);
1228 * Implements the internal {@link IWallpaperService} interface to convert
1229 * incoming calls to it back to calls on an {@link WallpaperService}.
1231 class IWallpaperServiceWrapper extends IWallpaperService.Stub {
1232 private final WallpaperService mTarget;
1234 public IWallpaperServiceWrapper(WallpaperService context) {
1239 public void attach(IWallpaperConnection conn, IBinder windowToken,
1240 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) {
1241 new IWallpaperEngineWrapper(mTarget, conn, windowToken,
1242 windowType, isPreview, reqWidth, reqHeight, padding);
1247 public void onCreate() {
1252 public void onDestroy() {
1254 for (int i=0; i<mActiveEngines.size(); i++) {
1255 mActiveEngines.get(i).detach();
1257 mActiveEngines.clear();
1261 * Implement to return the implementation of the internal accessibility
1262 * service interface. Subclasses should not override.
1265 public final IBinder onBind(Intent intent) {
1266 return new IWallpaperServiceWrapper(this);
1270 * Must be implemented to return a new instance of the wallpaper's engine.
1271 * Note that multiple instances may be active at the same time, such as
1272 * when the wallpaper is currently set as the active wallpaper and the user
1273 * is in the wallpaper picker viewing a preview of it as well.
1275 public abstract Engine onCreateEngine();
1278 protected void dump(FileDescriptor fd, PrintWriter out, String[] args) {
1279 out.print("State of wallpaper "); out.print(this); out.println(":");
1280 for (int i=0; i<mActiveEngines.size(); i++) {
1281 Engine engine = mActiveEngines.get(i);
1282 out.print(" Engine "); out.print(engine); out.println(":");
1283 engine.dump(" ", fd, out, args);