Home | History | Annotate | Download | only in view
      1 /*
      2  * Copyright (C) 2006 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.app.ActivityManager;
     20 import android.content.ComponentCallbacks2;
     21 import android.content.res.CompatibilityInfo;
     22 import android.content.res.Configuration;
     23 import android.graphics.PixelFormat;
     24 import android.opengl.ManagedEGLContext;
     25 import android.os.IBinder;
     26 import android.os.SystemProperties;
     27 import android.util.AndroidRuntimeException;
     28 import android.util.Log;
     29 import android.view.inputmethod.InputMethodManager;
     30 
     31 import java.io.FileDescriptor;
     32 import java.io.FileOutputStream;
     33 import java.io.PrintWriter;
     34 import java.util.HashMap;
     35 
     36 final class WindowLeaked extends AndroidRuntimeException {
     37     public WindowLeaked(String msg) {
     38         super(msg);
     39     }
     40 }
     41 
     42 /**
     43  * Low-level communication with the global system window manager.  It implements
     44  * the ViewManager interface, allowing you to add any View subclass as a
     45  * top-level window on the screen. Additional window manager specific layout
     46  * parameters are defined for control over how windows are displayed.
     47  * It also implemens the WindowManager interface, allowing you to control the
     48  * displays attached to the device.
     49  *
     50  * <p>Applications will not normally use WindowManager directly, instead relying
     51  * on the higher-level facilities in {@link android.app.Activity} and
     52  * {@link android.app.Dialog}.
     53  *
     54  * <p>Even for low-level window manager access, it is almost never correct to use
     55  * this class.  For example, {@link android.app.Activity#getWindowManager}
     56  * provides a ViewManager for adding windows that are associated with that
     57  * activity -- the window manager will not normally allow you to add arbitrary
     58  * windows that are not associated with an activity.
     59  *
     60  * @hide
     61  */
     62 public class WindowManagerImpl implements WindowManager {
     63     /**
     64      * The user is navigating with keys (not the touch screen), so
     65      * navigational focus should be shown.
     66      */
     67     public static final int RELAYOUT_RES_IN_TOUCH_MODE = 0x1;
     68     /**
     69      * This is the first time the window is being drawn,
     70      * so the client must call drawingFinished() when done
     71      */
     72     public static final int RELAYOUT_RES_FIRST_TIME = 0x2;
     73     /**
     74      * The window manager has changed the surface from the last call.
     75      */
     76     public static final int RELAYOUT_RES_SURFACE_CHANGED = 0x4;
     77     /**
     78      * The window manager is currently animating.  It will call
     79      * IWindow.doneAnimating() when done.
     80      */
     81     public static final int RELAYOUT_RES_ANIMATING = 0x8;
     82 
     83     /**
     84      * Flag for relayout: the client will be later giving
     85      * internal insets; as a result, the window will not impact other window
     86      * layouts until the insets are given.
     87      */
     88     public static final int RELAYOUT_INSETS_PENDING = 0x1;
     89 
     90     /**
     91      * Flag for relayout: the client may be currently using the current surface,
     92      * so if it is to be destroyed as a part of the relayout the destroy must
     93      * be deferred until later.  The client will call performDeferredDestroy()
     94      * when it is okay.
     95      */
     96     public static final int RELAYOUT_DEFER_SURFACE_DESTROY = 0x2;
     97 
     98     public static final int ADD_FLAG_APP_VISIBLE = 0x2;
     99     public static final int ADD_FLAG_IN_TOUCH_MODE = RELAYOUT_RES_IN_TOUCH_MODE;
    100 
    101     public static final int ADD_OKAY = 0;
    102     public static final int ADD_BAD_APP_TOKEN = -1;
    103     public static final int ADD_BAD_SUBWINDOW_TOKEN = -2;
    104     public static final int ADD_NOT_APP_TOKEN = -3;
    105     public static final int ADD_APP_EXITING = -4;
    106     public static final int ADD_DUPLICATE_ADD = -5;
    107     public static final int ADD_STARTING_NOT_NEEDED = -6;
    108     public static final int ADD_MULTIPLE_SINGLETON = -7;
    109     public static final int ADD_PERMISSION_DENIED = -8;
    110 
    111     private View[] mViews;
    112     private ViewRootImpl[] mRoots;
    113     private WindowManager.LayoutParams[] mParams;
    114     private boolean mNeedsEglTerminate;
    115 
    116     private Runnable mSystemPropertyUpdater = null;
    117 
    118     private final static Object sLock = new Object();
    119     private final static WindowManagerImpl sWindowManager = new WindowManagerImpl();
    120     private final static HashMap<CompatibilityInfo, WindowManager> sCompatWindowManagers
    121             = new HashMap<CompatibilityInfo, WindowManager>();
    122 
    123     static class CompatModeWrapper implements WindowManager {
    124         private final WindowManagerImpl mWindowManager;
    125         private final Display mDefaultDisplay;
    126         private final CompatibilityInfoHolder mCompatibilityInfo;
    127 
    128         CompatModeWrapper(WindowManager wm, CompatibilityInfoHolder ci) {
    129             mWindowManager = wm instanceof CompatModeWrapper
    130                     ? ((CompatModeWrapper)wm).mWindowManager : (WindowManagerImpl)wm;
    131 
    132             // Use the original display if there is no compatibility mode
    133             // to apply, or the underlying window manager is already a
    134             // compatibility mode wrapper.  (We assume that if it is a
    135             // wrapper, it is applying the same compatibility mode.)
    136             if (ci == null) {
    137                 mDefaultDisplay = mWindowManager.getDefaultDisplay();
    138             } else {
    139                 //mDefaultDisplay = mWindowManager.getDefaultDisplay();
    140                 mDefaultDisplay = Display.createCompatibleDisplay(
    141                         mWindowManager.getDefaultDisplay().getDisplayId(), ci);
    142             }
    143 
    144             mCompatibilityInfo = ci;
    145         }
    146 
    147         @Override
    148         public void addView(View view, android.view.ViewGroup.LayoutParams params) {
    149             mWindowManager.addView(view, params, mCompatibilityInfo);
    150         }
    151 
    152         @Override
    153         public void updateViewLayout(View view, android.view.ViewGroup.LayoutParams params) {
    154             mWindowManager.updateViewLayout(view, params);
    155 
    156         }
    157 
    158         @Override
    159         public void removeView(View view) {
    160             mWindowManager.removeView(view);
    161         }
    162 
    163         @Override
    164         public Display getDefaultDisplay() {
    165             return mDefaultDisplay;
    166         }
    167 
    168         @Override
    169         public void removeViewImmediate(View view) {
    170             mWindowManager.removeViewImmediate(view);
    171         }
    172 
    173         @Override
    174         public boolean isHardwareAccelerated() {
    175             return mWindowManager.isHardwareAccelerated();
    176         }
    177 
    178     }
    179 
    180     public static WindowManagerImpl getDefault() {
    181         return sWindowManager;
    182     }
    183 
    184     public static WindowManager getDefault(CompatibilityInfo compatInfo) {
    185         CompatibilityInfoHolder cih = new CompatibilityInfoHolder();
    186         cih.set(compatInfo);
    187         if (cih.getIfNeeded() == null) {
    188             return sWindowManager;
    189         }
    190 
    191         synchronized (sLock) {
    192             // NOTE: It would be cleaner to move the implementation of
    193             // WindowManagerImpl into a static inner class, and have this
    194             // public impl just call into that.  Then we can make multiple
    195             // instances of WindowManagerImpl for compat mode rather than
    196             // having to make wrappers.
    197             WindowManager wm = sCompatWindowManagers.get(compatInfo);
    198             if (wm == null) {
    199                 wm = new CompatModeWrapper(sWindowManager, cih);
    200                 sCompatWindowManagers.put(compatInfo, wm);
    201             }
    202             return wm;
    203         }
    204     }
    205 
    206     public static WindowManager getDefault(CompatibilityInfoHolder compatInfo) {
    207         return new CompatModeWrapper(sWindowManager, compatInfo);
    208     }
    209 
    210     public boolean isHardwareAccelerated() {
    211         return false;
    212     }
    213 
    214     public void addView(View view) {
    215         addView(view, new WindowManager.LayoutParams(
    216             WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.OPAQUE));
    217     }
    218 
    219     public void addView(View view, ViewGroup.LayoutParams params) {
    220         addView(view, params, null, false);
    221     }
    222 
    223     public void addView(View view, ViewGroup.LayoutParams params, CompatibilityInfoHolder cih) {
    224         addView(view, params, cih, false);
    225     }
    226 
    227     private void addView(View view, ViewGroup.LayoutParams params,
    228             CompatibilityInfoHolder cih, boolean nest) {
    229         if (false) Log.v("WindowManager", "addView view=" + view);
    230 
    231         if (!(params instanceof WindowManager.LayoutParams)) {
    232             throw new IllegalArgumentException(
    233                     "Params must be WindowManager.LayoutParams");
    234         }
    235 
    236         final WindowManager.LayoutParams wparams
    237                 = (WindowManager.LayoutParams)params;
    238 
    239         ViewRootImpl root;
    240         View panelParentView = null;
    241 
    242         synchronized (this) {
    243             // Start watching for system property changes.
    244             if (mSystemPropertyUpdater == null) {
    245                 mSystemPropertyUpdater = new Runnable() {
    246                     @Override public void run() {
    247                         synchronized (this) {
    248                             synchronized (this) {
    249                                 for (ViewRootImpl root : mRoots) {
    250                                     root.loadSystemProperties();
    251                                 }
    252                             }
    253                         }
    254                     }
    255                 };
    256                 SystemProperties.addChangeCallback(mSystemPropertyUpdater);
    257             }
    258 
    259             // Here's an odd/questionable case: if someone tries to add a
    260             // view multiple times, then we simply bump up a nesting count
    261             // and they need to remove the view the corresponding number of
    262             // times to have it actually removed from the window manager.
    263             // This is useful specifically for the notification manager,
    264             // which can continually add/remove the same view as a
    265             // notification gets updated.
    266             int index = findViewLocked(view, false);
    267             if (index >= 0) {
    268                 if (!nest) {
    269                     throw new IllegalStateException("View " + view
    270                             + " has already been added to the window manager.");
    271                 }
    272                 root = mRoots[index];
    273                 root.mAddNesting++;
    274                 // Update layout parameters.
    275                 view.setLayoutParams(wparams);
    276                 root.setLayoutParams(wparams, true);
    277                 return;
    278             }
    279 
    280             // If this is a panel window, then find the window it is being
    281             // attached to for future reference.
    282             if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
    283                     wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
    284                 final int count = mViews != null ? mViews.length : 0;
    285                 for (int i=0; i<count; i++) {
    286                     if (mRoots[i].mWindow.asBinder() == wparams.token) {
    287                         panelParentView = mViews[i];
    288                     }
    289                 }
    290             }
    291 
    292             root = new ViewRootImpl(view.getContext());
    293             root.mAddNesting = 1;
    294             if (cih == null) {
    295                 root.mCompatibilityInfo = new CompatibilityInfoHolder();
    296             } else {
    297                 root.mCompatibilityInfo = cih;
    298             }
    299 
    300             view.setLayoutParams(wparams);
    301 
    302             if (mViews == null) {
    303                 index = 1;
    304                 mViews = new View[1];
    305                 mRoots = new ViewRootImpl[1];
    306                 mParams = new WindowManager.LayoutParams[1];
    307             } else {
    308                 index = mViews.length + 1;
    309                 Object[] old = mViews;
    310                 mViews = new View[index];
    311                 System.arraycopy(old, 0, mViews, 0, index-1);
    312                 old = mRoots;
    313                 mRoots = new ViewRootImpl[index];
    314                 System.arraycopy(old, 0, mRoots, 0, index-1);
    315                 old = mParams;
    316                 mParams = new WindowManager.LayoutParams[index];
    317                 System.arraycopy(old, 0, mParams, 0, index-1);
    318             }
    319             index--;
    320 
    321             mViews[index] = view;
    322             mRoots[index] = root;
    323             mParams[index] = wparams;
    324         }
    325         // do this last because it fires off messages to start doing things
    326         root.setView(view, wparams, panelParentView);
    327     }
    328 
    329     public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
    330         if (!(params instanceof WindowManager.LayoutParams)) {
    331             throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
    332         }
    333 
    334         final WindowManager.LayoutParams wparams
    335                 = (WindowManager.LayoutParams)params;
    336 
    337         view.setLayoutParams(wparams);
    338 
    339         synchronized (this) {
    340             int index = findViewLocked(view, true);
    341             ViewRootImpl root = mRoots[index];
    342             mParams[index] = wparams;
    343             root.setLayoutParams(wparams, false);
    344         }
    345     }
    346 
    347     public void removeView(View view) {
    348         synchronized (this) {
    349             int index = findViewLocked(view, true);
    350             View curView = removeViewLocked(index);
    351             if (curView == view) {
    352                 return;
    353             }
    354 
    355             throw new IllegalStateException("Calling with view " + view
    356                     + " but the ViewAncestor is attached to " + curView);
    357         }
    358     }
    359 
    360     public void removeViewImmediate(View view) {
    361         synchronized (this) {
    362             int index = findViewLocked(view, true);
    363             ViewRootImpl root = mRoots[index];
    364             View curView = root.getView();
    365 
    366             root.mAddNesting = 0;
    367 
    368             if (view != null) {
    369                 InputMethodManager imm = InputMethodManager.getInstance(view.getContext());
    370                 if (imm != null) {
    371                     imm.windowDismissed(mViews[index].getWindowToken());
    372                 }
    373             }
    374 
    375             root.die(true);
    376             finishRemoveViewLocked(curView, index);
    377             if (curView == view) {
    378                 return;
    379             }
    380 
    381             throw new IllegalStateException("Calling with view " + view
    382                     + " but the ViewAncestor is attached to " + curView);
    383         }
    384     }
    385 
    386     View removeViewLocked(int index) {
    387         ViewRootImpl root = mRoots[index];
    388         View view = root.getView();
    389 
    390         // Don't really remove until we have matched all calls to add().
    391         root.mAddNesting--;
    392         if (root.mAddNesting > 0) {
    393             return view;
    394         }
    395 
    396         if (view != null) {
    397             InputMethodManager imm = InputMethodManager.getInstance(view.getContext());
    398             if (imm != null) {
    399                 imm.windowDismissed(mViews[index].getWindowToken());
    400             }
    401         }
    402         root.die(false);
    403         finishRemoveViewLocked(view, index);
    404         return view;
    405     }
    406 
    407     void finishRemoveViewLocked(View view, int index) {
    408         final int count = mViews.length;
    409 
    410         // remove it from the list
    411         View[] tmpViews = new View[count-1];
    412         removeItem(tmpViews, mViews, index);
    413         mViews = tmpViews;
    414 
    415         ViewRootImpl[] tmpRoots = new ViewRootImpl[count-1];
    416         removeItem(tmpRoots, mRoots, index);
    417         mRoots = tmpRoots;
    418 
    419         WindowManager.LayoutParams[] tmpParams
    420                 = new WindowManager.LayoutParams[count-1];
    421         removeItem(tmpParams, mParams, index);
    422         mParams = tmpParams;
    423 
    424         if (view != null) {
    425             view.assignParent(null);
    426             // func doesn't allow null...  does it matter if we clear them?
    427             //view.setLayoutParams(null);
    428         }
    429     }
    430 
    431     public void closeAll(IBinder token, String who, String what) {
    432         synchronized (this) {
    433             if (mViews == null)
    434                 return;
    435 
    436             int count = mViews.length;
    437             //Log.i("foo", "Closing all windows of " + token);
    438             for (int i=0; i<count; i++) {
    439                 //Log.i("foo", "@ " + i + " token " + mParams[i].token
    440                 //        + " view " + mRoots[i].getView());
    441                 if (token == null || mParams[i].token == token) {
    442                     ViewRootImpl root = mRoots[i];
    443                     root.mAddNesting = 1;
    444 
    445                     //Log.i("foo", "Force closing " + root);
    446                     if (who != null) {
    447                         WindowLeaked leak = new WindowLeaked(
    448                                 what + " " + who + " has leaked window "
    449                                 + root.getView() + " that was originally added here");
    450                         leak.setStackTrace(root.getLocation().getStackTrace());
    451                         Log.e("WindowManager", leak.getMessage(), leak);
    452                     }
    453 
    454                     removeViewLocked(i);
    455                     i--;
    456                     count--;
    457                 }
    458             }
    459         }
    460     }
    461 
    462     /**
    463      * @param level See {@link android.content.ComponentCallbacks}
    464      *
    465      * @hide
    466      */
    467     public void startTrimMemory(int level) {
    468         if (HardwareRenderer.isAvailable()) {
    469             // On low-end gfx devices we trim when memory is moderate;
    470             // on high-end devices we do this when low.
    471             if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE
    472                     || (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE
    473                             && !ActivityManager.isHighEndGfx(getDefaultDisplay()))) {
    474                 // Destroy all hardware surfaces and resources associated to
    475                 // known windows
    476                 synchronized (this) {
    477                     if (mViews == null) return;
    478                     int count = mViews.length;
    479                     for (int i = 0; i < count; i++) {
    480                         mRoots[i].terminateHardwareResources();
    481                     }
    482                 }
    483                 // Force a full memory flush
    484                 mNeedsEglTerminate = true;
    485                 HardwareRenderer.startTrimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
    486                 return;
    487             }
    488 
    489             HardwareRenderer.startTrimMemory(level);
    490         }
    491     }
    492 
    493     /**
    494      * @hide
    495      */
    496     public void endTrimMemory() {
    497         HardwareRenderer.endTrimMemory();
    498 
    499         if (mNeedsEglTerminate) {
    500             ManagedEGLContext.doTerminate();
    501             mNeedsEglTerminate = false;
    502         }
    503     }
    504 
    505     /**
    506      * @hide
    507      */
    508     public void trimLocalMemory() {
    509         synchronized (this) {
    510             if (mViews == null) return;
    511             int count = mViews.length;
    512             for (int i = 0; i < count; i++) {
    513                 mRoots[i].destroyHardwareLayers();
    514             }
    515         }
    516     }
    517 
    518     /**
    519      * @hide
    520      */
    521     public void dumpGfxInfo(FileDescriptor fd) {
    522         FileOutputStream fout = new FileOutputStream(fd);
    523         PrintWriter pw = new PrintWriter(fout);
    524         try {
    525             synchronized (this) {
    526                 if (mViews != null) {
    527                     final int count = mViews.length;
    528 
    529                     pw.println("Profile data in ms:");
    530 
    531                     for (int i = 0; i < count; i++) {
    532                         ViewRootImpl root = mRoots[i];
    533                         String name = getWindowName(root);
    534                         pw.printf("\n\t%s", name);
    535 
    536                         HardwareRenderer renderer = root.getView().mAttachInfo.mHardwareRenderer;
    537                         if (renderer != null) {
    538                             renderer.dumpGfxInfo(pw);
    539                         }
    540                     }
    541 
    542                     pw.println("\nView hierarchy:\n");
    543 
    544                     int viewsCount = 0;
    545                     int displayListsSize = 0;
    546                     int[] info = new int[2];
    547 
    548                     for (int i = 0; i < count; i++) {
    549                         ViewRootImpl root = mRoots[i];
    550                         root.dumpGfxInfo(info);
    551 
    552                         String name = getWindowName(root);
    553                         pw.printf("  %s\n  %d views, %.2f kB of display lists",
    554                                 name, info[0], info[1] / 1024.0f);
    555                         HardwareRenderer renderer = root.getView().mAttachInfo.mHardwareRenderer;
    556                         if (renderer != null) {
    557                             pw.printf(", %d frames rendered", renderer.getFrameCount());
    558                         }
    559                         pw.printf("\n\n");
    560 
    561                         viewsCount += info[0];
    562                         displayListsSize += info[1];
    563                     }
    564 
    565                     pw.printf("\nTotal ViewRootImpl: %d\n", count);
    566                     pw.printf("Total Views:        %d\n", viewsCount);
    567                     pw.printf("Total DisplayList:  %.2f kB\n\n", displayListsSize / 1024.0f);
    568                 }
    569             }
    570         } finally {
    571             pw.flush();
    572         }
    573     }
    574 
    575     private static String getWindowName(ViewRootImpl root) {
    576         return root.mWindowAttributes.getTitle() + "/" +
    577                 root.getClass().getName() + '@' + Integer.toHexString(root.hashCode());
    578     }
    579 
    580     public void setStoppedState(IBinder token, boolean stopped) {
    581         synchronized (this) {
    582             if (mViews == null)
    583                 return;
    584             int count = mViews.length;
    585             for (int i=0; i<count; i++) {
    586                 if (token == null || mParams[i].token == token) {
    587                     ViewRootImpl root = mRoots[i];
    588                     root.setStopped(stopped);
    589                 }
    590             }
    591         }
    592     }
    593 
    594     public void reportNewConfiguration(Configuration config) {
    595         synchronized (this) {
    596             int count = mViews.length;
    597             config = new Configuration(config);
    598             for (int i=0; i<count; i++) {
    599                 ViewRootImpl root = mRoots[i];
    600                 root.requestUpdateConfiguration(config);
    601             }
    602         }
    603     }
    604 
    605     public WindowManager.LayoutParams getRootViewLayoutParameter(View view) {
    606         ViewParent vp = view.getParent();
    607         while (vp != null && !(vp instanceof ViewRootImpl)) {
    608             vp = vp.getParent();
    609         }
    610 
    611         if (vp == null) return null;
    612 
    613         ViewRootImpl vr = (ViewRootImpl)vp;
    614 
    615         int N = mRoots.length;
    616         for (int i = 0; i < N; ++i) {
    617             if (mRoots[i] == vr) {
    618                 return mParams[i];
    619             }
    620         }
    621 
    622         return null;
    623     }
    624 
    625     public void closeAll() {
    626         closeAll(null, null, null);
    627     }
    628 
    629     public Display getDefaultDisplay() {
    630         return new Display(Display.DEFAULT_DISPLAY, null);
    631     }
    632 
    633     private static void removeItem(Object[] dst, Object[] src, int index) {
    634         if (dst.length > 0) {
    635             if (index > 0) {
    636                 System.arraycopy(src, 0, dst, 0, index);
    637             }
    638             if (index < dst.length) {
    639                 System.arraycopy(src, index+1, dst, index, src.length-index-1);
    640             }
    641         }
    642     }
    643 
    644     private int findViewLocked(View view, boolean required) {
    645         synchronized (this) {
    646             final int count = mViews != null ? mViews.length : 0;
    647             for (int i=0; i<count; i++) {
    648                 if (mViews[i] == view) {
    649                     return i;
    650                 }
    651             }
    652             if (required) {
    653                 throw new IllegalArgumentException(
    654                         "View not attached to window manager");
    655             }
    656             return -1;
    657         }
    658     }
    659 }
    660