OLD | NEW |
| (Empty) |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 package org.chromium.content.browser; | |
6 | |
7 import android.content.Context; | |
8 import android.os.Bundle; | |
9 | |
10 import org.chromium.base.Log; | |
11 import org.chromium.base.VisibleForTesting; | |
12 import org.chromium.base.process_launcher.ChildProcessCreationParams; | |
13 | |
14 /** | |
15 * ManagedChildProcessConnection is a connection to a child service that can hol
d several bindings | |
16 * to the service so it can be more or less agressively protected against OOM. | |
17 * Accessed from the launcher thread. (but for isOomProtectedOrWasWhenDied()). | |
18 */ | |
19 public class ManagedChildProcessConnection extends BaseChildProcessConnection { | |
20 private static final String TAG = "ManChildProcessConn"; | |
21 | |
22 public static final Factory FACTORY = new BaseChildProcessConnection.Factory
() { | |
23 @Override | |
24 public BaseChildProcessConnection create(Context context, int number, bo
olean sandboxed, | |
25 DeathCallback deathCallback, String serviceClassName, | |
26 Bundle childProcessCommonParameters, ChildProcessCreationParams
creationParams) { | |
27 assert LauncherThread.runningOnLauncherThread(); | |
28 return new ManagedChildProcessConnection(context, number, sandboxed,
deathCallback, | |
29 serviceClassName, childProcessCommonParameters, creationPara
ms); | |
30 } | |
31 }; | |
32 | |
33 // Initial binding protects the newly spawned process from being killed befo
re it is put to use, | |
34 // it is maintained between calls to start() and removeInitialBinding(). | |
35 private final ChildServiceConnection mInitialBinding; | |
36 | |
37 // Strong binding will make the service priority equal to the priority of th
e activity. We want | |
38 // the OS to be able to kill background renderers as it kills other backgrou
nd apps, so strong | |
39 // bindings are maintained only for services that are active at the moment (
between | |
40 // addStrongBinding() and removeStrongBinding()). | |
41 private final ChildServiceConnection mStrongBinding; | |
42 | |
43 // Low priority binding maintained in the entire lifetime of the connection,
i.e. between calls | |
44 // to start() and stop(). | |
45 private final ChildServiceConnection mWaivedBinding; | |
46 | |
47 // Incremented on addStrongBinding(), decremented on removeStrongBinding(). | |
48 private int mStrongBindingCount; | |
49 | |
50 // Moderate binding will make the service priority equal to the priority of
a visible process | |
51 // while the app is in the foreground. It will stay bound only while the app
is in the | |
52 // foreground to protect a background process from the system out-of-memory
killer. | |
53 private final ChildServiceConnection mModerateBinding; | |
54 | |
55 // Indicates whether the connection is OOM protected (if the connection is u
nbound, it contains | |
56 // the state at time of unbinding). | |
57 private boolean mOomProtected; | |
58 | |
59 // Set to true once unbind() was called. | |
60 private boolean mUnbound; | |
61 | |
62 @VisibleForTesting | |
63 ManagedChildProcessConnection(Context context, int number, boolean sandboxed
, | |
64 DeathCallback deathCallback, String serviceClassName, | |
65 Bundle childProcessCommonParameters, ChildProcessCreationParams crea
tionParams) { | |
66 super(context, number, sandboxed, deathCallback, serviceClassName, | |
67 childProcessCommonParameters, creationParams); | |
68 | |
69 int initialFlags = Context.BIND_AUTO_CREATE; | |
70 int extraBindFlags = shouldBindAsExportedService() ? Context.BIND_EXTERN
AL_SERVICE : 0; | |
71 mInitialBinding = createServiceConnection(initialFlags | extraBindFlags)
; | |
72 mStrongBinding = createServiceConnection( | |
73 Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT | extraBindFla
gs); | |
74 mWaivedBinding = createServiceConnection( | |
75 Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY | extraBi
ndFlags); | |
76 mModerateBinding = createServiceConnection(Context.BIND_AUTO_CREATE | ex
traBindFlags); | |
77 } | |
78 | |
79 @Override | |
80 protected boolean bind() { | |
81 assert LauncherThread.runningOnLauncherThread(); | |
82 assert !mUnbound; | |
83 if (!mInitialBinding.bind()) { | |
84 return false; | |
85 } | |
86 updateOomProtectedState(); | |
87 mWaivedBinding.bind(); | |
88 return true; | |
89 } | |
90 | |
91 @Override | |
92 public void unbind() { | |
93 assert LauncherThread.runningOnLauncherThread(); | |
94 mUnbound = true; | |
95 mInitialBinding.unbind(); | |
96 mStrongBinding.unbind(); | |
97 // Note that we don't update the OOM state here as to preserve the last
OOM state. | |
98 mWaivedBinding.unbind(); | |
99 mModerateBinding.unbind(); | |
100 mStrongBindingCount = 0; | |
101 } | |
102 | |
103 public boolean isInitialBindingBound() { | |
104 assert LauncherThread.runningOnLauncherThread(); | |
105 return mInitialBinding.isBound(); | |
106 } | |
107 | |
108 public boolean isStrongBindingBound() { | |
109 assert LauncherThread.runningOnLauncherThread(); | |
110 return mStrongBinding.isBound(); | |
111 } | |
112 | |
113 public void removeInitialBinding() { | |
114 assert LauncherThread.runningOnLauncherThread(); | |
115 mInitialBinding.unbind(); | |
116 updateOomProtectedState(); | |
117 } | |
118 | |
119 /** | |
120 * @return true if the connection is bound and OOM protected or was OOM prot
ected when unbound. | |
121 */ | |
122 public boolean isOomProtectedOrWasWhenDied() { | |
123 // WARNING: this method can be called from a thread other than the launc
her thread. | |
124 // Note that it returns the current OOM protected state and is racy. Thi
s not really | |
125 // preventable without changing the caller's API, short of blocking. | |
126 return mOomProtected; | |
127 } | |
128 | |
129 public void dropOomBindings() { | |
130 assert LauncherThread.runningOnLauncherThread(); | |
131 mInitialBinding.unbind(); | |
132 | |
133 mStrongBindingCount = 0; | |
134 mStrongBinding.unbind(); | |
135 updateOomProtectedState(); | |
136 | |
137 mModerateBinding.unbind(); | |
138 } | |
139 | |
140 public void addStrongBinding() { | |
141 assert LauncherThread.runningOnLauncherThread(); | |
142 if (!isConnected()) { | |
143 Log.w(TAG, "The connection is not bound for %d", getPid()); | |
144 return; | |
145 } | |
146 if (mStrongBindingCount == 0) { | |
147 mStrongBinding.bind(); | |
148 updateOomProtectedState(); | |
149 } | |
150 mStrongBindingCount++; | |
151 } | |
152 | |
153 public void removeStrongBinding() { | |
154 assert LauncherThread.runningOnLauncherThread(); | |
155 if (!isConnected()) { | |
156 Log.w(TAG, "The connection is not bound for %d", getPid()); | |
157 return; | |
158 } | |
159 assert mStrongBindingCount > 0; | |
160 mStrongBindingCount--; | |
161 if (mStrongBindingCount == 0) { | |
162 mStrongBinding.unbind(); | |
163 updateOomProtectedState(); | |
164 } | |
165 updateOomProtectedState(); | |
166 } | |
167 | |
168 public boolean isModerateBindingBound() { | |
169 assert LauncherThread.runningOnLauncherThread(); | |
170 return mModerateBinding.isBound(); | |
171 } | |
172 | |
173 public void addModerateBinding() { | |
174 assert LauncherThread.runningOnLauncherThread(); | |
175 if (!isConnected()) { | |
176 Log.w(TAG, "The connection is not bound for %d", getPid()); | |
177 return; | |
178 } | |
179 mModerateBinding.bind(); | |
180 } | |
181 | |
182 public void removeModerateBinding() { | |
183 assert LauncherThread.runningOnLauncherThread(); | |
184 if (!isConnected()) { | |
185 Log.w(TAG, "The connection is not bound for %d", getPid()); | |
186 return; | |
187 } | |
188 mModerateBinding.unbind(); | |
189 } | |
190 | |
191 // Should be called every time the mInitialBinding or mStrongBinding are bou
nd/unbound. | |
192 private void updateOomProtectedState() { | |
193 if (!mUnbound) { | |
194 mOomProtected = mInitialBinding.isBound() || mStrongBinding.isBound(
); | |
195 } | |
196 } | |
197 } | |
OLD | NEW |