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