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