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