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