1 /* 2 * Copyright (C) 2012 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.view; 18 19 import android.animation.ValueAnimator; 20 import android.app.ActivityManager; 21 import android.content.ComponentCallbacks2; 22 import android.content.res.Configuration; 23 import android.opengl.ManagedEGLContext; 24 import android.os.IBinder; 25 import android.os.Looper; 26 import android.os.RemoteException; 27 import android.os.ServiceManager; 28 import android.os.SystemProperties; 29 import android.util.AndroidRuntimeException; 30 import android.util.Log; 31 import android.view.inputmethod.InputMethodManager; 32 33 import java.io.FileDescriptor; 34 import java.io.FileOutputStream; 35 import java.io.PrintWriter; 36 37 /** 38 * Provides low-level communication with the system window manager for 39 * operations that are not associated with any particular context. 40 * 41 * This class is only used internally to implement global functions where 42 * the caller already knows the display and relevant compatibility information 43 * for the operation. For most purposes, you should use {@link WindowManager} instead 44 * since it is bound to a context. 45 * 46 * @see WindowManagerImpl 47 * @hide 48 */ 49 public final class WindowManagerGlobal { 50 private static final String TAG = "WindowManager"; 51 52 /** 53 * The user is navigating with keys (not the touch screen), so 54 * navigational focus should be shown. 55 */ 56 public static final int RELAYOUT_RES_IN_TOUCH_MODE = 0x1; 57 58 /** 59 * This is the first time the window is being drawn, 60 * so the client must call drawingFinished() when done 61 */ 62 public static final int RELAYOUT_RES_FIRST_TIME = 0x2; 63 64 /** 65 * The window manager has changed the surface from the last call. 66 */ 67 public static final int RELAYOUT_RES_SURFACE_CHANGED = 0x4; 68 69 /** 70 * The window manager is currently animating. It will call 71 * IWindow.doneAnimating() when done. 72 */ 73 public static final int RELAYOUT_RES_ANIMATING = 0x8; 74 75 /** 76 * Flag for relayout: the client will be later giving 77 * internal insets; as a result, the window will not impact other window 78 * layouts until the insets are given. 79 */ 80 public static final int RELAYOUT_INSETS_PENDING = 0x1; 81 82 /** 83 * Flag for relayout: the client may be currently using the current surface, 84 * so if it is to be destroyed as a part of the relayout the destroy must 85 * be deferred until later. The client will call performDeferredDestroy() 86 * when it is okay. 87 */ 88 public static final int RELAYOUT_DEFER_SURFACE_DESTROY = 0x2; 89 90 public static final int ADD_FLAG_APP_VISIBLE = 0x2; 91 public static final int ADD_FLAG_IN_TOUCH_MODE = RELAYOUT_RES_IN_TOUCH_MODE; 92 93 public static final int ADD_OKAY = 0; 94 public static final int ADD_BAD_APP_TOKEN = -1; 95 public static final int ADD_BAD_SUBWINDOW_TOKEN = -2; 96 public static final int ADD_NOT_APP_TOKEN = -3; 97 public static final int ADD_APP_EXITING = -4; 98 public static final int ADD_DUPLICATE_ADD = -5; 99 public static final int ADD_STARTING_NOT_NEEDED = -6; 100 public static final int ADD_MULTIPLE_SINGLETON = -7; 101 public static final int ADD_PERMISSION_DENIED = -8; 102 public static final int ADD_INVALID_DISPLAY = -9; 103 104 private static WindowManagerGlobal sDefaultWindowManager; 105 private static IWindowManager sWindowManagerService; 106 private static IWindowSession sWindowSession; 107 108 private final Object mLock = new Object(); 109 110 private View[] mViews; 111 private ViewRootImpl[] mRoots; 112 private WindowManager.LayoutParams[] mParams; 113 private boolean mNeedsEglTerminate; 114 115 private Runnable mSystemPropertyUpdater; 116 117 private WindowManagerGlobal() { 118 } 119 120 public static WindowManagerGlobal getInstance() { 121 synchronized (WindowManagerGlobal.class) { 122 if (sDefaultWindowManager == null) { 123 sDefaultWindowManager = new WindowManagerGlobal(); 124 } 125 return sDefaultWindowManager; 126 } 127 } 128 129 public static IWindowManager getWindowManagerService() { 130 synchronized (WindowManagerGlobal.class) { 131 if (sWindowManagerService == null) { 132 sWindowManagerService = IWindowManager.Stub.asInterface( 133 ServiceManager.getService("window")); 134 } 135 return sWindowManagerService; 136 } 137 } 138 139 public static IWindowSession getWindowSession() { 140 synchronized (WindowManagerGlobal.class) { 141 if (sWindowSession == null) { 142 try { 143 InputMethodManager imm = InputMethodManager.getInstance(); 144 IWindowManager windowManager = getWindowManagerService(); 145 sWindowSession = windowManager.openSession( 146 imm.getClient(), imm.getInputContext()); 147 float animatorScale = windowManager.getAnimationScale(2); 148 ValueAnimator.setDurationScale(animatorScale); 149 } catch (RemoteException e) { 150 Log.e(TAG, "Failed to open window session", e); 151 } 152 } 153 return sWindowSession; 154 } 155 } 156 157 public static IWindowSession peekWindowSession() { 158 synchronized (WindowManagerGlobal.class) { 159 return sWindowSession; 160 } 161 } 162 163 public String[] getViewRootNames() { 164 synchronized (mLock) { 165 if (mRoots == null) return new String[0]; 166 String[] mViewRoots = new String[mRoots.length]; 167 int i = 0; 168 for (ViewRootImpl root : mRoots) { 169 mViewRoots[i++] = getWindowName(root); 170 } 171 return mViewRoots; 172 } 173 } 174 175 public View getRootView(String name) { 176 synchronized (mLock) { 177 if (mRoots == null) return null; 178 for (ViewRootImpl root : mRoots) { 179 if (name.equals(getWindowName(root))) return root.getView(); 180 } 181 } 182 183 return null; 184 } 185 186 public void addView(View view, ViewGroup.LayoutParams params, 187 Display display, Window parentWindow) { 188 if (view == null) { 189 throw new IllegalArgumentException("view must not be null"); 190 } 191 if (display == null) { 192 throw new IllegalArgumentException("display must not be null"); 193 } 194 if (!(params instanceof WindowManager.LayoutParams)) { 195 throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); 196 } 197 198 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; 199 if (parentWindow != null) { 200 parentWindow.adjustLayoutParamsForSubWindow(wparams); 201 } 202 203 ViewRootImpl root; 204 View panelParentView = null; 205 206 synchronized (mLock) { 207 // Start watching for system property changes. 208 if (mSystemPropertyUpdater == null) { 209 mSystemPropertyUpdater = new Runnable() { 210 @Override public void run() { 211 synchronized (mLock) { 212 for (ViewRootImpl viewRoot : mRoots) { 213 viewRoot.loadSystemProperties(); 214 } 215 } 216 } 217 }; 218 SystemProperties.addChangeCallback(mSystemPropertyUpdater); 219 } 220 221 int index = findViewLocked(view, false); 222 if (index >= 0) { 223 throw new IllegalStateException("View " + view 224 + " has already been added to the window manager."); 225 } 226 227 // If this is a panel window, then find the window it is being 228 // attached to for future reference. 229 if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && 230 wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { 231 final int count = mViews != null ? mViews.length : 0; 232 for (int i=0; i<count; i++) { 233 if (mRoots[i].mWindow.asBinder() == wparams.token) { 234 panelParentView = mViews[i]; 235 } 236 } 237 } 238 239 root = new ViewRootImpl(view.getContext(), display); 240 241 view.setLayoutParams(wparams); 242 243 if (mViews == null) { 244 index = 1; 245 mViews = new View[1]; 246 mRoots = new ViewRootImpl[1]; 247 mParams = new WindowManager.LayoutParams[1]; 248 } else { 249 index = mViews.length + 1; 250 Object[] old = mViews; 251 mViews = new View[index]; 252 System.arraycopy(old, 0, mViews, 0, index-1); 253 old = mRoots; 254 mRoots = new ViewRootImpl[index]; 255 System.arraycopy(old, 0, mRoots, 0, index-1); 256 old = mParams; 257 mParams = new WindowManager.LayoutParams[index]; 258 System.arraycopy(old, 0, mParams, 0, index-1); 259 } 260 index--; 261 262 mViews[index] = view; 263 mRoots[index] = root; 264 mParams[index] = wparams; 265 } 266 267 // do this last because it fires off messages to start doing things 268 try { 269 root.setView(view, wparams, panelParentView); 270 } catch (RuntimeException e) { 271 // BadTokenException or InvalidDisplayException, clean up. 272 synchronized (mLock) { 273 final int index = findViewLocked(view, false); 274 if (index >= 0) { 275 removeViewLocked(index, true); 276 } 277 } 278 throw e; 279 } 280 } 281 282 public void updateViewLayout(View view, ViewGroup.LayoutParams params) { 283 if (view == null) { 284 throw new IllegalArgumentException("view must not be null"); 285 } 286 if (!(params instanceof WindowManager.LayoutParams)) { 287 throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); 288 } 289 290 final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params; 291 292 view.setLayoutParams(wparams); 293 294 synchronized (mLock) { 295 int index = findViewLocked(view, true); 296 ViewRootImpl root = mRoots[index]; 297 mParams[index] = wparams; 298 root.setLayoutParams(wparams, false); 299 } 300 } 301 302 public void removeView(View view, boolean immediate) { 303 if (view == null) { 304 throw new IllegalArgumentException("view must not be null"); 305 } 306 307 synchronized (mLock) { 308 int index = findViewLocked(view, true); 309 View curView = removeViewLocked(index, immediate); 310 if (curView == view) { 311 return; 312 } 313 314 throw new IllegalStateException("Calling with view " + view 315 + " but the ViewAncestor is attached to " + curView); 316 } 317 } 318 319 public void closeAll(IBinder token, String who, String what) { 320 synchronized (mLock) { 321 if (mViews == null) 322 return; 323 324 int count = mViews.length; 325 //Log.i("foo", "Closing all windows of " + token); 326 for (int i=0; i<count; i++) { 327 //Log.i("foo", "@ " + i + " token " + mParams[i].token 328 // + " view " + mRoots[i].getView()); 329 if (token == null || mParams[i].token == token) { 330 ViewRootImpl root = mRoots[i]; 331 332 //Log.i("foo", "Force closing " + root); 333 if (who != null) { 334 WindowLeaked leak = new WindowLeaked( 335 what + " " + who + " has leaked window " 336 + root.getView() + " that was originally added here"); 337 leak.setStackTrace(root.getLocation().getStackTrace()); 338 Log.e(TAG, leak.getMessage(), leak); 339 } 340 341 removeViewLocked(i, false); 342 i--; 343 count--; 344 } 345 } 346 } 347 } 348 349 private View removeViewLocked(int index, boolean immediate) { 350 ViewRootImpl root = mRoots[index]; 351 View view = root.getView(); 352 353 if (view != null) { 354 InputMethodManager imm = InputMethodManager.getInstance(); 355 if (imm != null) { 356 imm.windowDismissed(mViews[index].getWindowToken()); 357 } 358 } 359 root.die(immediate); 360 361 final int count = mViews.length; 362 363 // remove it from the list 364 View[] tmpViews = new View[count-1]; 365 removeItem(tmpViews, mViews, index); 366 mViews = tmpViews; 367 368 ViewRootImpl[] tmpRoots = new ViewRootImpl[count-1]; 369 removeItem(tmpRoots, mRoots, index); 370 mRoots = tmpRoots; 371 372 WindowManager.LayoutParams[] tmpParams 373 = new WindowManager.LayoutParams[count-1]; 374 removeItem(tmpParams, mParams, index); 375 mParams = tmpParams; 376 377 if (view != null) { 378 view.assignParent(null); 379 // func doesn't allow null... does it matter if we clear them? 380 //view.setLayoutParams(null); 381 } 382 return view; 383 } 384 385 private static void removeItem(Object[] dst, Object[] src, int index) { 386 if (dst.length > 0) { 387 if (index > 0) { 388 System.arraycopy(src, 0, dst, 0, index); 389 } 390 if (index < dst.length) { 391 System.arraycopy(src, index+1, dst, index, src.length-index-1); 392 } 393 } 394 } 395 396 private int findViewLocked(View view, boolean required) { 397 if (mViews != null) { 398 final int count = mViews.length; 399 for (int i = 0; i < count; i++) { 400 if (mViews[i] == view) { 401 return i; 402 } 403 } 404 } 405 if (required) { 406 throw new IllegalArgumentException("View not attached to window manager"); 407 } 408 return -1; 409 } 410 411 public void startTrimMemory(int level) { 412 if (HardwareRenderer.isAvailable()) { 413 // On low-end gfx devices we trim when memory is moderate; 414 // on high-end devices we do this when low. 415 if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE 416 || (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE 417 && !ActivityManager.isHighEndGfx())) { 418 // Destroy all hardware surfaces and resources associated to 419 // known windows 420 synchronized (mLock) { 421 if (mViews == null) return; 422 int count = mViews.length; 423 for (int i = 0; i < count; i++) { 424 mRoots[i].terminateHardwareResources(); 425 } 426 } 427 // Force a full memory flush 428 mNeedsEglTerminate = true; 429 HardwareRenderer.startTrimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE); 430 return; 431 } 432 433 HardwareRenderer.startTrimMemory(level); 434 } 435 } 436 437 public void endTrimMemory() { 438 HardwareRenderer.endTrimMemory(); 439 440 if (mNeedsEglTerminate) { 441 ManagedEGLContext.doTerminate(); 442 mNeedsEglTerminate = false; 443 } 444 } 445 446 public void trimLocalMemory() { 447 synchronized (mLock) { 448 if (mViews == null) return; 449 int count = mViews.length; 450 for (int i = 0; i < count; i++) { 451 mRoots[i].destroyHardwareLayers(); 452 } 453 } 454 } 455 456 public void dumpGfxInfo(FileDescriptor fd) { 457 FileOutputStream fout = new FileOutputStream(fd); 458 PrintWriter pw = new PrintWriter(fout); 459 try { 460 synchronized (mLock) { 461 if (mViews != null) { 462 final int count = mViews.length; 463 464 pw.println("Profile data in ms:"); 465 466 for (int i = 0; i < count; i++) { 467 ViewRootImpl root = mRoots[i]; 468 String name = getWindowName(root); 469 pw.printf("\n\t%s", name); 470 471 HardwareRenderer renderer = 472 root.getView().mAttachInfo.mHardwareRenderer; 473 if (renderer != null) { 474 renderer.dumpGfxInfo(pw); 475 } 476 } 477 478 pw.println("\nView hierarchy:\n"); 479 480 int viewsCount = 0; 481 int displayListsSize = 0; 482 int[] info = new int[2]; 483 484 for (int i = 0; i < count; i++) { 485 ViewRootImpl root = mRoots[i]; 486 root.dumpGfxInfo(info); 487 488 String name = getWindowName(root); 489 pw.printf(" %s\n %d views, %.2f kB of display lists", 490 name, info[0], info[1] / 1024.0f); 491 HardwareRenderer renderer = 492 root.getView().mAttachInfo.mHardwareRenderer; 493 if (renderer != null) { 494 pw.printf(", %d frames rendered", renderer.getFrameCount()); 495 } 496 pw.printf("\n\n"); 497 498 viewsCount += info[0]; 499 displayListsSize += info[1]; 500 } 501 502 pw.printf("\nTotal ViewRootImpl: %d\n", count); 503 pw.printf("Total Views: %d\n", viewsCount); 504 pw.printf("Total DisplayList: %.2f kB\n\n", displayListsSize / 1024.0f); 505 } 506 } 507 } finally { 508 pw.flush(); 509 } 510 } 511 512 private static String getWindowName(ViewRootImpl root) { 513 return root.mWindowAttributes.getTitle() + "/" + 514 root.getClass().getName() + '@' + Integer.toHexString(root.hashCode()); 515 } 516 517 public void setStoppedState(IBinder token, boolean stopped) { 518 synchronized (mLock) { 519 if (mViews != null) { 520 int count = mViews.length; 521 for (int i=0; i < count; i++) { 522 if (token == null || mParams[i].token == token) { 523 ViewRootImpl root = mRoots[i]; 524 root.setStopped(stopped); 525 } 526 } 527 } 528 } 529 } 530 531 public void reportNewConfiguration(Configuration config) { 532 synchronized (mLock) { 533 if (mViews != null) { 534 int count = mViews.length; 535 config = new Configuration(config); 536 for (int i=0; i < count; i++) { 537 ViewRootImpl root = mRoots[i]; 538 root.requestUpdateConfiguration(config); 539 } 540 } 541 } 542 } 543 } 544 545 final class WindowLeaked extends AndroidRuntimeException { 546 public WindowLeaked(String msg) { 547 super(msg); 548 } 549 } 550