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