WallpaperService.java revision d052a9416ae3f7e42fc1e7de0740021df385ee48
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(prefix); 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, mStableInsets, 679 mInputChannel) < 0) { 680 Log.w(TAG, "Failed to add window while updating wallpaper surface."); 681 return; 682 } 683 mCreated = true; 684 685 mInputEventReceiver = new WallpaperInputEventReceiver( 686 mInputChannel, Looper.myLooper()); 687 } 688 689 mSurfaceHolder.mSurfaceLock.lock(); 690 mDrawingAllowed = true; 691 692 if (!fixedSize) { 693 mLayout.surfaceInsets.set(mIWallpaperEngine.mDisplayPadding); 694 } else { 695 mLayout.surfaceInsets.set(0, 0, 0, 0); 696 } 697 final int relayoutResult = mSession.relayout( 698 mWindow, mWindow.mSeq, mLayout, mWidth, mHeight, 699 View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets, 700 mVisibleInsets, mStableInsets, mConfiguration, mSurfaceHolder.mSurface); 701 702 if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface 703 + ", frame=" + mWinFrame); 704 705 int w = mWinFrame.width(); 706 int h = mWinFrame.height(); 707 708 if (!fixedSize) { 709 final Rect padding = mIWallpaperEngine.mDisplayPadding; 710 w += padding.left + padding.right; 711 h += padding.top + padding.bottom; 712 mOverscanInsets.left += padding.left; 713 mOverscanInsets.top += padding.top; 714 mOverscanInsets.right += padding.right; 715 mOverscanInsets.bottom += padding.bottom; 716 mContentInsets.left += padding.left; 717 mContentInsets.top += padding.top; 718 mContentInsets.right += padding.right; 719 mContentInsets.bottom += padding.bottom; 720 mStableInsets.left += padding.left; 721 mStableInsets.top += padding.top; 722 mStableInsets.right += padding.right; 723 mStableInsets.bottom += padding.bottom; 724 } 725 726 if (mCurWidth != w) { 727 sizeChanged = true; 728 mCurWidth = w; 729 } 730 if (mCurHeight != h) { 731 sizeChanged = true; 732 mCurHeight = h; 733 } 734 735 insetsChanged |= !mDispatchedOverscanInsets.equals(mOverscanInsets); 736 insetsChanged |= !mDispatchedContentInsets.equals(mContentInsets); 737 insetsChanged |= !mDispatchedStableInsets.equals(mStableInsets); 738 739 mSurfaceHolder.setSurfaceFrameSize(w, h); 740 mSurfaceHolder.mSurfaceLock.unlock(); 741 742 if (!mSurfaceHolder.mSurface.isValid()) { 743 reportSurfaceDestroyed(); 744 if (DEBUG) Log.v(TAG, "Layout: Surface destroyed"); 745 return; 746 } 747 748 boolean didSurface = false; 749 750 try { 751 mSurfaceHolder.ungetCallbacks(); 752 753 if (surfaceCreating) { 754 mIsCreating = true; 755 didSurface = true; 756 if (DEBUG) Log.v(TAG, "onSurfaceCreated(" 757 + mSurfaceHolder + "): " + this); 758 onSurfaceCreated(mSurfaceHolder); 759 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 760 if (callbacks != null) { 761 for (SurfaceHolder.Callback c : callbacks) { 762 c.surfaceCreated(mSurfaceHolder); 763 } 764 } 765 } 766 767 redrawNeeded |= creating || (relayoutResult 768 & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0; 769 770 if (forceReport || creating || surfaceCreating 771 || formatChanged || sizeChanged) { 772 if (DEBUG) { 773 RuntimeException e = new RuntimeException(); 774 e.fillInStackTrace(); 775 Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating 776 + " formatChanged=" + formatChanged 777 + " sizeChanged=" + sizeChanged, e); 778 } 779 if (DEBUG) Log.v(TAG, "onSurfaceChanged(" 780 + mSurfaceHolder + ", " + mFormat 781 + ", " + mCurWidth + ", " + mCurHeight 782 + "): " + this); 783 didSurface = true; 784 onSurfaceChanged(mSurfaceHolder, mFormat, 785 mCurWidth, mCurHeight); 786 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 787 if (callbacks != null) { 788 for (SurfaceHolder.Callback c : callbacks) { 789 c.surfaceChanged(mSurfaceHolder, mFormat, 790 mCurWidth, mCurHeight); 791 } 792 } 793 } 794 795 if (insetsChanged) { 796 mDispatchedOverscanInsets.set(mOverscanInsets); 797 mDispatchedContentInsets.set(mContentInsets); 798 mDispatchedStableInsets.set(mStableInsets); 799 final boolean isRound = (mIsEmulator && mIsCircularEmulator) 800 || mWindowIsRound; 801 mFinalSystemInsets.set(mDispatchedOverscanInsets); 802 mFinalStableInsets.set(mDispatchedStableInsets); 803 if (mOutsetBottom != null) { 804 final DisplayMetrics metrics = getResources().getDisplayMetrics(); 805 mFinalSystemInsets.bottom = 806 ( (int) mOutsetBottom.getDimension(metrics) ) 807 + mIWallpaperEngine.mDisplayPadding.bottom; 808 } 809 WindowInsets insets = new WindowInsets(mFinalSystemInsets, 810 null, mFinalStableInsets, isRound); 811 onApplyWindowInsets(insets); 812 } 813 814 if (redrawNeeded) { 815 onSurfaceRedrawNeeded(mSurfaceHolder); 816 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 817 if (callbacks != null) { 818 for (SurfaceHolder.Callback c : callbacks) { 819 if (c instanceof SurfaceHolder.Callback2) { 820 ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( 821 mSurfaceHolder); 822 } 823 } 824 } 825 } 826 827 if (didSurface && !mReportedVisible) { 828 // This wallpaper is currently invisible, but its 829 // surface has changed. At this point let's tell it 830 // again that it is invisible in case the report about 831 // the surface caused it to start running. We really 832 // don't want wallpapers running when not visible. 833 if (mIsCreating) { 834 // Some wallpapers will ignore this call if they 835 // had previously been told they were invisble, 836 // so if we are creating a new surface then toggle 837 // the state to get them to notice. 838 if (DEBUG) Log.v(TAG, "onVisibilityChanged(true) at surface: " 839 + this); 840 onVisibilityChanged(true); 841 } 842 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false) at surface: " 843 + this); 844 onVisibilityChanged(false); 845 } 846 847 } finally { 848 mIsCreating = false; 849 mSurfaceCreated = true; 850 if (redrawNeeded) { 851 mSession.finishDrawing(mWindow); 852 } 853 mIWallpaperEngine.reportShown(); 854 } 855 } catch (RemoteException ex) { 856 } 857 if (DEBUG) Log.v( 858 TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + 859 " w=" + mLayout.width + " h=" + mLayout.height); 860 } 861 } 862 863 void attach(IWallpaperEngineWrapper wrapper) { 864 if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper); 865 if (mDestroyed) { 866 return; 867 } 868 869 mIWallpaperEngine = wrapper; 870 mCaller = wrapper.mCaller; 871 mConnection = wrapper.mConnection; 872 mWindowToken = wrapper.mWindowToken; 873 mSurfaceHolder.setSizeFromLayout(); 874 mInitializing = true; 875 mSession = WindowManagerGlobal.getWindowSession(); 876 877 mWindow.setSession(mSession); 878 879 mScreenOn = ((PowerManager)getSystemService(Context.POWER_SERVICE)).isScreenOn(); 880 881 IntentFilter filter = new IntentFilter(); 882 filter.addAction(Intent.ACTION_SCREEN_ON); 883 filter.addAction(Intent.ACTION_SCREEN_OFF); 884 registerReceiver(mReceiver, filter); 885 886 if (DEBUG) Log.v(TAG, "onCreate(): " + this); 887 onCreate(mSurfaceHolder); 888 889 mInitializing = false; 890 mReportedVisible = false; 891 updateSurface(false, false, false); 892 } 893 894 void doDesiredSizeChanged(int desiredWidth, int desiredHeight) { 895 if (!mDestroyed) { 896 if (DEBUG) Log.v(TAG, "onDesiredSizeChanged(" 897 + desiredWidth + "," + desiredHeight + "): " + this); 898 mIWallpaperEngine.mReqWidth = desiredWidth; 899 mIWallpaperEngine.mReqHeight = desiredHeight; 900 onDesiredSizeChanged(desiredWidth, desiredHeight); 901 doOffsetsChanged(true); 902 } 903 } 904 905 void doDisplayPaddingChanged(Rect padding) { 906 if (!mDestroyed) { 907 if (DEBUG) Log.v(TAG, "onDisplayPaddingChanged(" + padding + "): " + this); 908 if (!mIWallpaperEngine.mDisplayPadding.equals(padding)) { 909 mIWallpaperEngine.mDisplayPadding.set(padding); 910 updateSurface(true, false, false); 911 } 912 } 913 } 914 915 void doVisibilityChanged(boolean visible) { 916 if (!mDestroyed) { 917 mVisible = visible; 918 reportVisibility(); 919 } 920 } 921 922 void reportVisibility() { 923 if (!mDestroyed) { 924 boolean visible = mVisible && mScreenOn; 925 if (mReportedVisible != visible) { 926 mReportedVisible = visible; 927 if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible 928 + "): " + this); 929 if (visible) { 930 // If becoming visible, in preview mode the surface 931 // may have been destroyed so now we need to make 932 // sure it is re-created. 933 doOffsetsChanged(false); 934 updateSurface(false, false, false); 935 } 936 onVisibilityChanged(visible); 937 } 938 } 939 } 940 941 void doOffsetsChanged(boolean always) { 942 if (mDestroyed) { 943 return; 944 } 945 946 if (!always && !mOffsetsChanged) { 947 return; 948 } 949 950 float xOffset; 951 float yOffset; 952 float xOffsetStep; 953 float yOffsetStep; 954 boolean sync; 955 synchronized (mLock) { 956 xOffset = mPendingXOffset; 957 yOffset = mPendingYOffset; 958 xOffsetStep = mPendingXOffsetStep; 959 yOffsetStep = mPendingYOffsetStep; 960 sync = mPendingSync; 961 mPendingSync = false; 962 mOffsetMessageEnqueued = false; 963 } 964 965 if (mSurfaceCreated) { 966 if (mReportedVisible) { 967 if (DEBUG) Log.v(TAG, "Offsets change in " + this 968 + ": " + xOffset + "," + yOffset); 969 final int availw = mIWallpaperEngine.mReqWidth-mCurWidth; 970 final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0; 971 final int availh = mIWallpaperEngine.mReqHeight-mCurHeight; 972 final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0; 973 onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels); 974 } else { 975 mOffsetsChanged = true; 976 } 977 } 978 979 if (sync) { 980 try { 981 if (DEBUG) Log.v(TAG, "Reporting offsets change complete"); 982 mSession.wallpaperOffsetsComplete(mWindow.asBinder()); 983 } catch (RemoteException e) { 984 } 985 } 986 } 987 988 void doCommand(WallpaperCommand cmd) { 989 Bundle result; 990 if (!mDestroyed) { 991 result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z, 992 cmd.extras, cmd.sync); 993 } else { 994 result = null; 995 } 996 if (cmd.sync) { 997 try { 998 if (DEBUG) Log.v(TAG, "Reporting command complete"); 999 mSession.wallpaperCommandComplete(mWindow.asBinder(), result); 1000 } catch (RemoteException e) { 1001 } 1002 } 1003 } 1004 1005 void reportSurfaceDestroyed() { 1006 if (mSurfaceCreated) { 1007 mSurfaceCreated = false; 1008 mSurfaceHolder.ungetCallbacks(); 1009 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); 1010 if (callbacks != null) { 1011 for (SurfaceHolder.Callback c : callbacks) { 1012 c.surfaceDestroyed(mSurfaceHolder); 1013 } 1014 } 1015 if (DEBUG) Log.v(TAG, "onSurfaceDestroyed(" 1016 + mSurfaceHolder + "): " + this); 1017 onSurfaceDestroyed(mSurfaceHolder); 1018 } 1019 } 1020 1021 void detach() { 1022 if (mDestroyed) { 1023 return; 1024 } 1025 1026 mDestroyed = true; 1027 1028 if (mVisible) { 1029 mVisible = false; 1030 if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this); 1031 onVisibilityChanged(false); 1032 } 1033 1034 reportSurfaceDestroyed(); 1035 1036 if (DEBUG) Log.v(TAG, "onDestroy(): " + this); 1037 onDestroy(); 1038 1039 unregisterReceiver(mReceiver); 1040 1041 if (mCreated) { 1042 try { 1043 if (DEBUG) Log.v(TAG, "Removing window and destroying surface " 1044 + mSurfaceHolder.getSurface() + " of: " + this); 1045 1046 if (mInputEventReceiver != null) { 1047 mInputEventReceiver.dispose(); 1048 mInputEventReceiver = null; 1049 } 1050 1051 mSession.remove(mWindow); 1052 } catch (RemoteException e) { 1053 } 1054 mSurfaceHolder.mSurface.release(); 1055 mCreated = false; 1056 1057 // Dispose the input channel after removing the window so the Window Manager 1058 // doesn't interpret the input channel being closed as an abnormal termination. 1059 if (mInputChannel != null) { 1060 mInputChannel.dispose(); 1061 mInputChannel = null; 1062 } 1063 } 1064 } 1065 } 1066 1067 class IWallpaperEngineWrapper extends IWallpaperEngine.Stub 1068 implements HandlerCaller.Callback { 1069 private final HandlerCaller mCaller; 1070 1071 final IWallpaperConnection mConnection; 1072 final IBinder mWindowToken; 1073 final int mWindowType; 1074 final boolean mIsPreview; 1075 boolean mShownReported; 1076 int mReqWidth; 1077 int mReqHeight; 1078 final Rect mDisplayPadding = new Rect(); 1079 1080 Engine mEngine; 1081 1082 IWallpaperEngineWrapper(WallpaperService context, 1083 IWallpaperConnection conn, IBinder windowToken, 1084 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) { 1085 mCaller = new HandlerCaller(context, context.getMainLooper(), this, true); 1086 mConnection = conn; 1087 mWindowToken = windowToken; 1088 mWindowType = windowType; 1089 mIsPreview = isPreview; 1090 mReqWidth = reqWidth; 1091 mReqHeight = reqHeight; 1092 mDisplayPadding.set(padding); 1093 1094 Message msg = mCaller.obtainMessage(DO_ATTACH); 1095 mCaller.sendMessage(msg); 1096 } 1097 1098 public void setDesiredSize(int width, int height) { 1099 Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height); 1100 mCaller.sendMessage(msg); 1101 } 1102 1103 public void setDisplayPadding(Rect padding) { 1104 Message msg = mCaller.obtainMessageO(DO_SET_DISPLAY_PADDING, padding); 1105 mCaller.sendMessage(msg); 1106 } 1107 1108 public void setVisibility(boolean visible) { 1109 Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, 1110 visible ? 1 : 0); 1111 mCaller.sendMessage(msg); 1112 } 1113 1114 public void dispatchPointer(MotionEvent event) { 1115 if (mEngine != null) { 1116 mEngine.dispatchPointer(event); 1117 } else { 1118 event.recycle(); 1119 } 1120 } 1121 1122 public void dispatchWallpaperCommand(String action, int x, int y, 1123 int z, Bundle extras) { 1124 if (mEngine != null) { 1125 mEngine.mWindow.dispatchWallpaperCommand(action, x, y, z, extras, false); 1126 } 1127 } 1128 1129 public void reportShown() { 1130 if (!mShownReported) { 1131 mShownReported = true; 1132 try { 1133 mConnection.engineShown(this); 1134 } catch (RemoteException e) { 1135 Log.w(TAG, "Wallpaper host disappeared", e); 1136 return; 1137 } 1138 } 1139 } 1140 1141 public void destroy() { 1142 Message msg = mCaller.obtainMessage(DO_DETACH); 1143 mCaller.sendMessage(msg); 1144 } 1145 1146 public void executeMessage(Message message) { 1147 switch (message.what) { 1148 case DO_ATTACH: { 1149 try { 1150 mConnection.attachEngine(this); 1151 } catch (RemoteException e) { 1152 Log.w(TAG, "Wallpaper host disappeared", e); 1153 return; 1154 } 1155 Engine engine = onCreateEngine(); 1156 mEngine = engine; 1157 mActiveEngines.add(engine); 1158 engine.attach(this); 1159 return; 1160 } 1161 case DO_DETACH: { 1162 mActiveEngines.remove(mEngine); 1163 mEngine.detach(); 1164 return; 1165 } 1166 case DO_SET_DESIRED_SIZE: { 1167 mEngine.doDesiredSizeChanged(message.arg1, message.arg2); 1168 return; 1169 } 1170 case DO_SET_DISPLAY_PADDING: { 1171 mEngine.doDisplayPaddingChanged((Rect) message.obj); 1172 } 1173 case MSG_UPDATE_SURFACE: 1174 mEngine.updateSurface(true, false, false); 1175 break; 1176 case MSG_VISIBILITY_CHANGED: 1177 if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine 1178 + ": " + message.arg1); 1179 mEngine.doVisibilityChanged(message.arg1 != 0); 1180 break; 1181 case MSG_WALLPAPER_OFFSETS: { 1182 mEngine.doOffsetsChanged(true); 1183 } break; 1184 case MSG_WALLPAPER_COMMAND: { 1185 WallpaperCommand cmd = (WallpaperCommand)message.obj; 1186 mEngine.doCommand(cmd); 1187 } break; 1188 case MSG_WINDOW_RESIZED: { 1189 final boolean reportDraw = message.arg1 != 0; 1190 mEngine.updateSurface(true, false, reportDraw); 1191 mEngine.doOffsetsChanged(true); 1192 } break; 1193 case MSG_WINDOW_MOVED: { 1194 // Do nothing. What does it mean for a Wallpaper to move? 1195 } break; 1196 case MSG_TOUCH_EVENT: { 1197 boolean skip = false; 1198 MotionEvent ev = (MotionEvent)message.obj; 1199 if (ev.getAction() == MotionEvent.ACTION_MOVE) { 1200 synchronized (mEngine.mLock) { 1201 if (mEngine.mPendingMove == ev) { 1202 mEngine.mPendingMove = null; 1203 } else { 1204 // this is not the motion event we are looking for.... 1205 skip = true; 1206 } 1207 } 1208 } 1209 if (!skip) { 1210 if (DEBUG) Log.v(TAG, "Delivering touch event: " + ev); 1211 mEngine.onTouchEvent(ev); 1212 } 1213 ev.recycle(); 1214 } break; 1215 default : 1216 Log.w(TAG, "Unknown message type " + message.what); 1217 } 1218 } 1219 } 1220 1221 /** 1222 * Implements the internal {@link IWallpaperService} interface to convert 1223 * incoming calls to it back to calls on an {@link WallpaperService}. 1224 */ 1225 class IWallpaperServiceWrapper extends IWallpaperService.Stub { 1226 private final WallpaperService mTarget; 1227 1228 public IWallpaperServiceWrapper(WallpaperService context) { 1229 mTarget = context; 1230 } 1231 1232 @Override 1233 public void attach(IWallpaperConnection conn, IBinder windowToken, 1234 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) { 1235 new IWallpaperEngineWrapper(mTarget, conn, windowToken, 1236 windowType, isPreview, reqWidth, reqHeight, padding); 1237 } 1238 } 1239 1240 @Override 1241 public void onCreate() { 1242 super.onCreate(); 1243 } 1244 1245 @Override 1246 public void onDestroy() { 1247 super.onDestroy(); 1248 for (int i=0; i<mActiveEngines.size(); i++) { 1249 mActiveEngines.get(i).detach(); 1250 } 1251 mActiveEngines.clear(); 1252 } 1253 1254 /** 1255 * Implement to return the implementation of the internal accessibility 1256 * service interface. Subclasses should not override. 1257 */ 1258 @Override 1259 public final IBinder onBind(Intent intent) { 1260 return new IWallpaperServiceWrapper(this); 1261 } 1262 1263 /** 1264 * Must be implemented to return a new instance of the wallpaper's engine. 1265 * Note that multiple instances may be active at the same time, such as 1266 * when the wallpaper is currently set as the active wallpaper and the user 1267 * is in the wallpaper picker viewing a preview of it as well. 1268 */ 1269 public abstract Engine onCreateEngine(); 1270 1271 @Override 1272 protected void dump(FileDescriptor fd, PrintWriter out, String[] args) { 1273 out.print("State of wallpaper "); out.print(this); out.println(":"); 1274 for (int i=0; i<mActiveEngines.size(); i++) { 1275 Engine engine = mActiveEngines.get(i); 1276 out.print(" Engine "); out.print(engine); out.println(":"); 1277 engine.dump(" ", fd, out, args); 1278 } 1279 } 1280} 1281