Commit 9bdb822f authored by Bo Liu's avatar Bo Liu Committed by Commit Bot

android: Android 10 isolated service bug workaround

Android 10 has a bug that UID used for non-primary user cannot
be freed correctly, eventually exhausting the pool of UIDs for
isolated services. There is a global pool of 1000 UIDs, and each
app zygote has a smaller pool of 100; the bug appplies to both cases.
The leaked UID in the app zygote pool are released when the zygote
is killed; leaked UIDs in the global pool are released when the
device is rebooted. So way to slightly delay until the device needs
to be rebooted is to use up the app zygote pool first before using
the non-zygote global pool.

Add a workaround allocator that's only used under secondary user
on Android 10. It will try to bind to a new service from the app
zygote. If bind fails, which happens if the UID pool is exhausted,
then try using the non app zygote service.

Bug: 1035432
Change-Id: I07cba74a21076e4cbc9342705f7b69b97e011c20
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2001998
Commit-Queue: Bo <boliu@chromium.org>
Reviewed-by: default avatarTommy Nyquist <nyquist@chromium.org>
Reviewed-by: default avatarssid <ssid@chromium.org>
Cr-Commit-Position: refs/heads/master@{#733829}
parent 70e21e06
...@@ -416,6 +416,27 @@ public class ChildProcessConnection { ...@@ -416,6 +416,27 @@ public class ChildProcessConnection {
} }
} }
// This is the same as start, but returns a boolean whether bind succeeded. Also on failure,
// no method is called on |serviceCallback| so the allocation can be tried again. This is
// package private and is meant to be used by Android10WorkaroundAllocatorImpl. See comment
// there for details.
boolean tryStart(boolean useStrongBinding, ServiceCallback serviceCallback) {
try {
TraceEvent.begin("ChildProcessConnection.tryStart");
assert isRunningOnLauncherThread();
assert mConnectionParams
== null : "setupConnection() called before start() in ChildProcessConnection.";
if (!bind(useStrongBinding)) {
return false;
}
mServiceCallback = serviceCallback;
} finally {
TraceEvent.end("ChildProcessConnection.tryStart");
}
return true;
}
/** /**
* Call bindService again on this connection. This must be called while connection is already * Call bindService again on this connection. This must be called while connection is already
* bound. This is useful for controlling the recency of this connection, and also for updating * bound. This is useful for controlling the recency of this connection, and also for updating
......
...@@ -138,6 +138,7 @@ public class ChildConnectionAllocatorTest { ...@@ -138,6 +138,7 @@ public class ChildConnectionAllocatorTest {
private ChildConnectionAllocator.FixedSizeAllocatorImpl mAllocator; private ChildConnectionAllocator.FixedSizeAllocatorImpl mAllocator;
private ChildConnectionAllocator mVariableSizeAllocator; private ChildConnectionAllocator mVariableSizeAllocator;
private ChildConnectionAllocator mWorkaroundAllocator;
@Before @Before
public void setUp() { public void setUp() {
...@@ -153,6 +154,12 @@ public class ChildConnectionAllocatorTest { ...@@ -153,6 +154,12 @@ public class ChildConnectionAllocatorTest {
true /* bindTocall */, false /* bindAsExternalService */, true /* bindTocall */, false /* bindAsExternalService */,
false /* useStrongBinding */, 10); false /* useStrongBinding */, 10);
mVariableSizeAllocator.setConnectionFactoryForTesting(mTestConnectionFactory); mVariableSizeAllocator.setConnectionFactoryForTesting(mTestConnectionFactory);
mWorkaroundAllocator = ChildConnectionAllocator.createWorkaroundForTesting(new Handler(),
TEST_PACKAGE_NAME, null /* freeSlotCallback */, "AllocatorTest",
true /* bindTocall */, false /* bindAsExternalService */,
false /* useStrongBinding */, 10);
mWorkaroundAllocator.setConnectionFactoryForTesting(mTestConnectionFactory);
} }
@Test @Test
...@@ -211,6 +218,16 @@ public class ChildConnectionAllocatorTest { ...@@ -211,6 +218,16 @@ public class ChildConnectionAllocatorTest {
doTestQueueAllocation(mVariableSizeAllocator, freeConnectionCallback); doTestQueueAllocation(mVariableSizeAllocator, freeConnectionCallback);
} }
@Test
@Feature({"ProcessManagement"})
public void testQueueAllocationWorkaround() {
Runnable freeConnectionCallback = mock(Runnable.class);
mWorkaroundAllocator = ChildConnectionAllocator.createWorkaroundForTesting(new Handler(),
TEST_PACKAGE_NAME, freeConnectionCallback, "AllocatorTest", true /* bindToCaller */,
false /* bindAsExternalService */, false /* useStrongBinding */, 1);
doTestQueueAllocation(mWorkaroundAllocator, freeConnectionCallback);
}
private void doTestQueueAllocation( private void doTestQueueAllocation(
ChildConnectionAllocator allocator, Runnable freeConnectionCallback) { ChildConnectionAllocator allocator, Runnable freeConnectionCallback) {
allocator.setConnectionFactoryForTesting(mTestConnectionFactory); allocator.setConnectionFactoryForTesting(mTestConnectionFactory);
...@@ -301,6 +318,13 @@ public class ChildConnectionAllocatorTest { ...@@ -301,6 +318,13 @@ public class ChildConnectionAllocatorTest {
false /* onChildStartFailed */, false /* onChildProcessDied */); false /* onChildStartFailed */, false /* onChildProcessDied */);
} }
@Test
@Feature({"ProcessManagement"})
public void testOnChildStartedCallbackWorkaround() {
runTestWithConnectionCallbacks(mWorkaroundAllocator, true /* onChildStarted */,
false /* onChildStartFailed */, false /* onChildProcessDied */);
}
@Test @Test
@Feature({"ProcessManagement"}) @Feature({"ProcessManagement"})
public void testOnChildStartFailedCallback() { public void testOnChildStartFailedCallback() {
...@@ -315,6 +339,13 @@ public class ChildConnectionAllocatorTest { ...@@ -315,6 +339,13 @@ public class ChildConnectionAllocatorTest {
true /* onChildStartFailed */, false /* onChildProcessDied */); true /* onChildStartFailed */, false /* onChildProcessDied */);
} }
@Test
@Feature({"ProcessManagement"})
public void testOnChildStartFailedCallbackWorkaround() {
runTestWithConnectionCallbacks(mWorkaroundAllocator, false /* onChildStarted */,
true /* onChildStartFailed */, false /* onChildProcessDied */);
}
@Test @Test
@Feature({"ProcessManagement"}) @Feature({"ProcessManagement"})
public void testOnChildProcessDiedCallback() { public void testOnChildProcessDiedCallback() {
...@@ -329,6 +360,13 @@ public class ChildConnectionAllocatorTest { ...@@ -329,6 +360,13 @@ public class ChildConnectionAllocatorTest {
false /* onChildStartFailed */, true /* onChildProcessDied */); false /* onChildStartFailed */, true /* onChildProcessDied */);
} }
@Test
@Feature({"ProcessManagement"})
public void testOnChildProcessDiedCallbackWorkaround() {
runTestWithConnectionCallbacks(mWorkaroundAllocator, false /* onChildStarted */,
false /* onChildStartFailed */, true /* onChildProcessDied */);
}
/** /**
* Tests that the allocator clears the connection when it fails to bind/process dies. * Tests that the allocator clears the connection when it fails to bind/process dies.
*/ */
...@@ -389,6 +427,12 @@ public class ChildConnectionAllocatorTest { ...@@ -389,6 +427,12 @@ public class ChildConnectionAllocatorTest {
testFreeConnection(mVariableSizeAllocator, FREE_CONNECTION_TEST_CALLBACK_START_FAILED); testFreeConnection(mVariableSizeAllocator, FREE_CONNECTION_TEST_CALLBACK_START_FAILED);
} }
@Test
@Feature({"ProcessManagement"})
public void testFreeConnectionOnChildStartFailedWorkaround() {
testFreeConnection(mWorkaroundAllocator, FREE_CONNECTION_TEST_CALLBACK_START_FAILED);
}
@Test @Test
@Feature({"ProcessManagement"}) @Feature({"ProcessManagement"})
public void testFreeConnectionOnChildProcessDied() { public void testFreeConnectionOnChildProcessDied() {
...@@ -400,4 +444,10 @@ public class ChildConnectionAllocatorTest { ...@@ -400,4 +444,10 @@ public class ChildConnectionAllocatorTest {
public void testFreeConnectionOnChildProcessDiedVariableSize() { public void testFreeConnectionOnChildProcessDiedVariableSize() {
testFreeConnection(mVariableSizeAllocator, FREE_CONNECTION_TEST_CALLBACK_PROCESS_DIED); testFreeConnection(mVariableSizeAllocator, FREE_CONNECTION_TEST_CALLBACK_PROCESS_DIED);
} }
@Test
@Feature({"ProcessManagement"})
public void testFreeConnectionOnChildProcessDiedWorkaround() {
testFreeConnection(mWorkaroundAllocator, FREE_CONNECTION_TEST_CALLBACK_PROCESS_DIED);
}
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment