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