From 648251710162cdaf7371012a1cbb79b9bc5bc0e4 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Wed, 2 Mar 2011 21:32:58 -0800 Subject: [PATCH] Fix issue #3485923: Gmail crash Allow application to try to recover if a surface OOM error happens on the client side. Change-Id: I0308bd99647a35e4bcac448340b7fc6330a828f6 --- .../android/app/ActivityManagerNative.java | 6 +- core/java/android/app/IActivityManager.java | 2 +- core/java/android/view/HardwareRenderer.java | 16 ++--- core/java/android/view/IWindowSession.aidl | 5 ++ core/java/android/view/ViewRoot.java | 66 ++++++++++++++++--- .../java/com/android/server/MountService.java | 2 +- .../server/am/ActivityManagerService.java | 11 +++- .../java/com/android/server/wm/Session.java | 4 ++ .../server/wm/WindowManagerService.java | 34 +++++++--- .../com/android/server/wm/WindowState.java | 4 +- .../bridge/android/BridgeWindowSession.java | 4 ++ 11 files changed, 122 insertions(+), 32 deletions(-) diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index d3d3792947892..64266354edcc6 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1020,7 +1020,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM data.enforceInterface(IActivityManager.descriptor); int[] pids = data.createIntArray(); String reason = data.readString(); - boolean res = killPids(pids, reason); + boolean secure = data.readInt() != 0; + boolean res = killPids(pids, reason, secure); reply.writeNoException(); reply.writeInt(res ? 1 : 0); return true; @@ -2636,12 +2637,13 @@ class ActivityManagerProxy implements IActivityManager mRemote.transact(NOTE_WAKEUP_ALARM_TRANSACTION, data, null, 0); data.recycle(); } - public boolean killPids(int[] pids, String reason) throws RemoteException { + public boolean killPids(int[] pids, String reason, boolean secure) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeIntArray(pids); data.writeString(reason); + data.writeInt(secure ? 1 : 0); mRemote.transact(KILL_PIDS_TRANSACTION, data, reply, 0); boolean res = reply.readInt() != 0; data.recycle(); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index f42e8fb73e86c..61e6fc8466c2a 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -250,7 +250,7 @@ public interface IActivityManager extends IInterface { public void noteWakeupAlarm(IIntentSender sender) throws RemoteException; - public boolean killPids(int[] pids, String reason) throws RemoteException; + public boolean killPids(int[] pids, String reason, boolean secure) throws RemoteException; // Special low-level communication with activity manager. public void startRunning(String pkg, String cls, String action, diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index d46dacc2e55db..b53aa21b955b3 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -108,14 +108,14 @@ public abstract class HardwareRenderer { * * @return True if the initialization was successful, false otherwise. */ - abstract boolean initialize(SurfaceHolder holder); + abstract boolean initialize(SurfaceHolder holder) throws Surface.OutOfResourcesException; /** * Updates the hardware renderer for the specified surface. * * @param holder The holder for the surface to hardware accelerate. */ - abstract void updateSurface(SurfaceHolder holder); + abstract void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException; /** * Setup the hardware renderer for drawing. This is called for every @@ -189,7 +189,7 @@ public abstract class HardwareRenderer { * @param holder */ void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo, - SurfaceHolder holder) { + SurfaceHolder holder) throws Surface.OutOfResourcesException { if (isRequested()) { // We lost the gl context, so recreate it. if (!isEnabled()) { @@ -366,7 +366,7 @@ public abstract class HardwareRenderer { } @Override - boolean initialize(SurfaceHolder holder) { + boolean initialize(SurfaceHolder holder) throws Surface.OutOfResourcesException { if (isRequested() && !isEnabled()) { initializeEgl(); mGl = createEglSurface(holder); @@ -395,7 +395,7 @@ public abstract class HardwareRenderer { } @Override - void updateSurface(SurfaceHolder holder) { + void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException { if (isRequested() && isEnabled()) { createEglSurface(holder); } @@ -446,7 +446,7 @@ public abstract class HardwareRenderer { sEglContext = createContext(sEgl, sEglDisplay, sEglConfig); } - GL createEglSurface(SurfaceHolder holder) { + GL createEglSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException { // Check preconditions. if (sEgl == null) { throw new RuntimeException("egl not initialized"); @@ -494,7 +494,7 @@ public abstract class HardwareRenderer { * the context is current and bound to a surface. */ if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) { - throw new RuntimeException("eglMakeCurrent failed " + throw new Surface.OutOfResourcesException("eglMakeCurrent failed " + getEGLErrorString(sEgl.eglGetError())); } @@ -516,7 +516,7 @@ public abstract class HardwareRenderer { @Override void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo, - SurfaceHolder holder) { + SurfaceHolder holder) throws Surface.OutOfResourcesException { if (isRequested()) { checkEglErrors(); super.initializeIfNeeded(width, height, attachInfo, holder); diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 1218e81d77081..990af083d859d 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -83,6 +83,11 @@ interface IWindowSession { out Rect outVisibleInsets, out Configuration outConfig, out Surface outSurface); + /** + * Called by a client to report that it ran out of graphics memory. + */ + boolean outOfMemory(IWindow window); + /** * Give the window manager a hint of the part of the window that is * completely transparent, allowing it to work with the surface flinger diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 965c959f2db4a..546823047b24e 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -1066,7 +1066,20 @@ public final class ViewRoot extends Handler implements ViewParent, mPreviousTransparentRegion.setEmpty(); if (mAttachInfo.mHardwareRenderer != null) { - hwInitialized = mAttachInfo.mHardwareRenderer.initialize(mHolder); + try { + hwInitialized = mAttachInfo.mHardwareRenderer.initialize(mHolder); + } catch (Surface.OutOfResourcesException e) { + Log.e(TAG, "OutOfResourcesException initializing HW surface", e); + try { + if (!sWindowSession.outOfMemory(mWindow)) { + Slog.w(TAG, "No processes killed for memory; killing self"); + Process.killProcess(Process.myPid()); + } + } catch (RemoteException ex) { + } + mLayoutRequested = true; // ask wm for a new surface next time. + return; + } } } } else if (!mSurface.isValid()) { @@ -1081,7 +1094,20 @@ public final class ViewRoot extends Handler implements ViewParent, } else if (surfaceGenerationId != mSurface.getGenerationId() && mSurfaceHolder == null && mAttachInfo.mHardwareRenderer != null) { fullRedrawNeeded = true; - mAttachInfo.mHardwareRenderer.updateSurface(mHolder); + try { + mAttachInfo.mHardwareRenderer.updateSurface(mHolder); + } catch (Surface.OutOfResourcesException e) { + Log.e(TAG, "OutOfResourcesException updating HW surface", e); + try { + if (!sWindowSession.outOfMemory(mWindow)) { + Slog.w(TAG, "No processes killed for memory; killing self"); + Process.killProcess(Process.myPid()); + } + } catch (RemoteException ex) { + } + mLayoutRequested = true; // ask wm for a new surface next time. + return; + } } } catch (RemoteException e) { } @@ -1569,14 +1595,24 @@ public final class ViewRoot extends Handler implements ViewParent, canvas.setDensity(mDensity); } catch (Surface.OutOfResourcesException e) { Log.e(TAG, "OutOfResourcesException locking surface", e); - // TODO: we should ask the window manager to do something! - // for now we just do nothing + try { + if (!sWindowSession.outOfMemory(mWindow)) { + Slog.w(TAG, "No processes killed for memory; killing self"); + Process.killProcess(Process.myPid()); + } + } catch (RemoteException ex) { + } mLayoutRequested = true; // ask wm for a new surface next time. return; } catch (IllegalArgumentException e) { Log.e(TAG, "IllegalArgumentException locking surface", e); - // TODO: we should ask the window manager to do something! - // for now we just do nothing + try { + if (!sWindowSession.outOfMemory(mWindow)) { + Slog.w(TAG, "No processes killed for memory; killing self"); + Process.killProcess(Process.myPid()); + } + } catch (RemoteException ex) { + } mLayoutRequested = true; // ask wm for a new surface next time. return; } @@ -2033,8 +2069,22 @@ public final class ViewRoot extends Handler implements ViewParent, if (mAttachInfo.mHardwareRenderer != null && mSurface != null && mSurface.isValid()) { mFullRedrawNeeded = true; - mAttachInfo.mHardwareRenderer.initializeIfNeeded(mWidth, mHeight, - mAttachInfo, mHolder); + try { + mAttachInfo.mHardwareRenderer.initializeIfNeeded(mWidth, mHeight, + mAttachInfo, mHolder); + } catch (Surface.OutOfResourcesException e) { + Log.e(TAG, "OutOfResourcesException locking surface", e); + try { + if (!sWindowSession.outOfMemory(mWindow)) { + Slog.w(TAG, "No processes killed for memory; killing self"); + Process.killProcess(Process.myPid()); + } + } catch (RemoteException ex) { + } + // Retry in a bit. + sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2), 500); + return; + } } } diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index 436eff0befbe4..91ada6bf52b1d 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -374,7 +374,7 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC done = true; } else { // Eliminate system process here? - ams.killPids(pids, "unmount media"); + ams.killPids(pids, "unmount media", true); // Confirm if file references have been freed. pids = getStorageUsers(path); if (pids == null || pids.length == 0) { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index e6dfb7f765e4c..2eafc1dc7c249 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -6184,7 +6184,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - public boolean killPids(int[] pids, String pReason) { + public boolean killPids(int[] pids, String pReason, boolean secure) { if (Binder.getCallingUid() != Process.SYSTEM_UID) { throw new SecurityException("killPids only available to the system"); } @@ -6207,11 +6207,18 @@ public final class ActivityManagerService extends ActivityManagerNative } } - // If the worse oom_adj is somewhere in the hidden proc LRU range, + // If the worst oom_adj is somewhere in the hidden proc LRU range, // then constrain it so we will kill all hidden procs. if (worstType < EMPTY_APP_ADJ && worstType > HIDDEN_APP_MIN_ADJ) { worstType = HIDDEN_APP_MIN_ADJ; } + + // If this is not a secure call, don't let it kill processes that + // are important. + if (!secure && worstType < SECONDARY_SERVER_ADJ) { + worstType = SECONDARY_SERVER_ADJ; + } + Slog.w(TAG, "Killing processes " + reason + " at adjustment " + worstType); for (int i=0; i