Merge "Test TetheringManager could be GC after getting connector"
This commit is contained in:
@@ -28,6 +28,7 @@ import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
|
|||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
@@ -46,6 +47,7 @@ import android.net.TetheringManager;
|
|||||||
import android.net.TetheringRequestParcel;
|
import android.net.TetheringRequestParcel;
|
||||||
import android.net.ip.IpServer;
|
import android.net.ip.IpServer;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.ConditionVariable;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.ResultReceiver;
|
import android.os.ResultReceiver;
|
||||||
@@ -63,7 +65,6 @@ import org.junit.Before;
|
|||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Matchers;
|
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
@@ -503,38 +504,81 @@ public final class TetheringServiceTest {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class ConnectorSupplier<T> implements Supplier<T> {
|
||||||
|
private T mResult = null;
|
||||||
|
|
||||||
|
public void set(T result) {
|
||||||
|
mResult = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get() {
|
||||||
|
return mResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void forceGc() {
|
||||||
|
System.gc();
|
||||||
|
System.runFinalization();
|
||||||
|
System.gc();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTetheringManagerLeak() throws Exception {
|
public void testTetheringManagerLeak() throws Exception {
|
||||||
runAsAccessNetworkState((none) -> {
|
runAsAccessNetworkState((none) -> {
|
||||||
final ArrayList<ITetheringEventCallback> callbacks = new ArrayList<>();
|
final ArrayList<ITetheringEventCallback> callbacks = new ArrayList<>();
|
||||||
|
final ConditionVariable registeredCv = new ConditionVariable(false);
|
||||||
doAnswer((invocation) -> {
|
doAnswer((invocation) -> {
|
||||||
final Object[] args = invocation.getArguments();
|
final Object[] args = invocation.getArguments();
|
||||||
callbacks.add((ITetheringEventCallback) args[0]);
|
callbacks.add((ITetheringEventCallback) args[0]);
|
||||||
|
registeredCv.open();
|
||||||
return null;
|
return null;
|
||||||
}).when(mTethering).registerTetheringEventCallback(Matchers.anyObject());
|
}).when(mTethering).registerTetheringEventCallback(any());
|
||||||
|
|
||||||
final Supplier<IBinder> supplier = () -> mMockConnector.getIBinder();
|
doAnswer((invocation) -> {
|
||||||
|
final Object[] args = invocation.getArguments();
|
||||||
|
callbacks.remove((ITetheringEventCallback) args[0]);
|
||||||
|
return null;
|
||||||
|
}).when(mTethering).unregisterTetheringEventCallback(any());
|
||||||
|
|
||||||
|
final ConnectorSupplier<IBinder> supplier = new ConnectorSupplier<>();
|
||||||
|
|
||||||
TetheringManager tm = new TetheringManager(mMockConnector.getService(), supplier);
|
TetheringManager tm = new TetheringManager(mMockConnector.getService(), supplier);
|
||||||
assertNotNull(tm);
|
assertNotNull(tm);
|
||||||
assertEquals("Internal callback is not registered", 1, callbacks.size());
|
assertEquals("Internal callback should not be registered", 0, callbacks.size());
|
||||||
|
|
||||||
final WeakReference weakTm = new WeakReference(tm);
|
final WeakReference<TetheringManager> weakTm = new WeakReference(tm);
|
||||||
assertNotNull(weakTm.get());
|
assertNotNull(weakTm.get());
|
||||||
|
|
||||||
|
// TetheringManager couldn't be GCed because pollingConnector thread implicitly
|
||||||
|
// reference TetheringManager object.
|
||||||
tm = null;
|
tm = null;
|
||||||
|
forceGc();
|
||||||
|
assertNotNull(weakTm.get());
|
||||||
|
|
||||||
|
// After getting connector, pollingConnector thread stops and internal callback is
|
||||||
|
// registered.
|
||||||
|
supplier.set(mMockConnector.getIBinder());
|
||||||
|
final long timeout = 500L;
|
||||||
|
if (!registeredCv.block(timeout)) {
|
||||||
|
fail("TetheringManager poll connector fail after " + timeout + " ms");
|
||||||
|
}
|
||||||
|
assertEquals("Internal callback is not registered", 1, callbacks.size());
|
||||||
|
assertNotNull(weakTm.get());
|
||||||
|
|
||||||
final int attempts = 100;
|
final int attempts = 100;
|
||||||
final long waitIntervalMs = 50;
|
final long waitIntervalMs = 50;
|
||||||
for (int i = 0; i < attempts; i++) {
|
for (int i = 0; i < attempts; i++) {
|
||||||
System.gc();
|
forceGc();
|
||||||
System.runFinalization();
|
|
||||||
System.gc();
|
|
||||||
if (weakTm.get() == null) break;
|
if (weakTm.get() == null) break;
|
||||||
|
|
||||||
Thread.sleep(waitIntervalMs);
|
Thread.sleep(waitIntervalMs);
|
||||||
}
|
}
|
||||||
assertNull("TetheringManager weak reference still not null after " + attempts
|
assertNull("TetheringManager weak reference still not null after " + attempts
|
||||||
+ " attempts", weakTm.get());
|
+ " attempts", weakTm.get());
|
||||||
|
|
||||||
|
// BUG: internal callback do not be unregistered after TetheringManager is GCed.
|
||||||
|
assertEquals(1, callbacks.size());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user