Refactor CountryDetectorService

Clean-up the test to use Mockito instead of the deprecated
AndroidTestCase, also getting rid of 1.5s of Thread.sleep.
No logic change.

Bug: 141626568
Test: atest CountryDetectorServiceTest
Merged-in: Ic0782b1ffcd1dcb30b9f1d91b37521d1f9887f03
Change-Id: Ic0782b1ffcd1dcb30b9f1d91b37521d1f9887f03
(cherry picked from commit 50361687ba)
This commit is contained in:
Christophe Koessler
2019-12-17 16:40:47 -08:00
parent b43b52a2e1
commit 611bad9556
3 changed files with 131 additions and 93 deletions

View File

@@ -16,14 +16,6 @@
package com.android.server;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.HashMap;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.server.location.ComprehensiveCountryDetector;
import android.content.Context;
import android.location.Country;
import android.location.CountryListener;
@@ -36,17 +28,25 @@ import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.server.location.ComprehensiveCountryDetector;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.HashMap;
/**
* This class detects the country that the user is in through
* {@link ComprehensiveCountryDetector}.
* This class detects the country that the user is in through {@link ComprehensiveCountryDetector}.
*
* @hide
*/
public class CountryDetectorService extends ICountryDetector.Stub implements Runnable {
public class CountryDetectorService extends ICountryDetector.Stub {
/**
* The class represents the remote listener, it will also removes itself
* from listener list when the remote process was died.
* The class represents the remote listener, it will also removes itself from listener list when
* the remote process was died.
*/
private final class Receiver implements IBinder.DeathRecipient {
private final ICountryListener mListener;
@@ -79,9 +79,11 @@ public class CountryDetectorService extends ICountryDetector.Stub implements Run
}
}
private final static String TAG = "CountryDetector";
private static final String TAG = "CountryDetector";
/** Whether to dump the state of the country detector service to bugreports */
/**
* Whether to dump the state of the country detector service to bugreports
*/
private static final boolean DEBUG = false;
private final HashMap<IBinder, Receiver> mReceivers;
@@ -92,15 +94,21 @@ public class CountryDetectorService extends ICountryDetector.Stub implements Run
private CountryListener mLocationBasedDetectorListener;
public CountryDetectorService(Context context) {
this(context, BackgroundThread.getHandler());
}
@VisibleForTesting
CountryDetectorService(Context context, Handler handler) {
super();
mReceivers = new HashMap<IBinder, Receiver>();
mReceivers = new HashMap<>();
mContext = context;
mHandler = handler;
}
@Override
public Country detectCountry() {
if (!mSystemReady) {
return null; // server not yet active
return null; // server not yet active
} else {
return mCountryDetector.detectCountry();
}
@@ -154,9 +162,8 @@ public class CountryDetectorService extends ICountryDetector.Stub implements Run
}
}
protected void notifyReceivers(Country country) {
synchronized(mReceivers) {
synchronized (mReceivers) {
for (Receiver receiver : mReceivers.values()) {
try {
receiver.getListener().onCountryDetected(country);
@@ -170,38 +177,23 @@ public class CountryDetectorService extends ICountryDetector.Stub implements Run
void systemRunning() {
// Shall we wait for the initialization finish.
BackgroundThread.getHandler().post(this);
mHandler.post(
() -> {
initialize();
mSystemReady = true;
});
}
private void initialize() {
mCountryDetector = new ComprehensiveCountryDetector(mContext);
mLocationBasedDetectorListener = new CountryListener() {
public void onCountryDetected(final Country country) {
mHandler.post(new Runnable() {
public void run() {
notifyReceivers(country);
}
});
}
};
}
public void run() {
mHandler = new Handler();
initialize();
mSystemReady = true;
mLocationBasedDetectorListener = country -> mHandler.post(() -> notifyReceivers(country));
}
protected void setCountryListener(final CountryListener listener) {
mHandler.post(new Runnable() {
@Override
public void run() {
mCountryDetector.setCountryListener(listener);
}
});
mHandler.post(() -> mCountryDetector.setCountryListener(listener));
}
// For testing
@VisibleForTesting
boolean isSystemReady() {
return mSystemReady;
}

View File

@@ -28,6 +28,7 @@ android_test {
"services.net",
"services.usage",
"guava",
"androidx.test.core",
"androidx.test.runner",
"androidx.test.rules",
"mockito-target-minus-junit4",

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010 The Android Open Source Project
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,28 +11,48 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
* limitations under the License.
*/
package com.android.server;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doAnswer;
import android.content.Context;
import android.location.Country;
import android.location.CountryListener;
import android.location.ICountryListener;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.test.AndroidTestCase;
public class CountryDetectorServiceTest extends AndroidTestCase {
private class CountryListenerTester extends ICountryListener.Stub {
import androidx.test.core.app.ApplicationProvider;
import com.google.common.truth.Expect;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class CountryDetectorServiceTest {
private static class CountryListenerTester extends ICountryListener.Stub {
private Country mCountry;
@Override
public void onCountryDetected(Country country) throws RemoteException {
public void onCountryDetected(Country country) {
mCountry = country;
}
public Country getCountry() {
Country getCountry() {
return mCountry;
}
@@ -41,12 +61,11 @@ public class CountryDetectorServiceTest extends AndroidTestCase {
}
}
private class CountryDetectorServiceTester extends CountryDetectorService {
private static class CountryDetectorServiceTester extends CountryDetectorService {
private CountryListener mListener;
public CountryDetectorServiceTester(Context context) {
super(context);
CountryDetectorServiceTester(Context context, Handler handler) {
super(context, handler);
}
@Override
@@ -59,51 +78,77 @@ public class CountryDetectorServiceTest extends AndroidTestCase {
mListener = listener;
}
public boolean isListenerSet() {
boolean isListenerSet() {
return mListener != null;
}
}
public void testAddRemoveListener() throws RemoteException {
CountryDetectorServiceTester serviceTester = new CountryDetectorServiceTester(getContext());
serviceTester.systemRunning();
waitForSystemReady(serviceTester);
CountryListenerTester listenerTester = new CountryListenerTester();
serviceTester.addCountryListener(listenerTester);
assertTrue(serviceTester.isListenerSet());
serviceTester.removeCountryListener(listenerTester);
assertFalse(serviceTester.isListenerSet());
}
@Rule
public final Expect expect = Expect.create();
@Spy
private Context mContext = ApplicationProvider.getApplicationContext();
@Spy
private Handler mHandler = new Handler(Looper.myLooper());
private CountryDetectorServiceTester mCountryDetectorService;
public void testNotifyListeners() throws RemoteException {
CountryDetectorServiceTester serviceTester = new CountryDetectorServiceTester(getContext());
CountryListenerTester listenerTesterA = new CountryListenerTester();
CountryListenerTester listenerTesterB = new CountryListenerTester();
Country country = new Country("US", Country.COUNTRY_SOURCE_NETWORK);
serviceTester.systemRunning();
waitForSystemReady(serviceTester);
serviceTester.addCountryListener(listenerTesterA);
serviceTester.addCountryListener(listenerTesterB);
serviceTester.notifyReceivers(country);
assertTrue(serviceTester.isListenerSet());
assertTrue(listenerTesterA.isNotified());
assertTrue(listenerTesterB.isNotified());
serviceTester.removeCountryListener(listenerTesterA);
serviceTester.removeCountryListener(listenerTesterB);
assertFalse(serviceTester.isListenerSet());
}
private void waitForSystemReady(CountryDetectorService service) {
int count = 5;
while (count-- > 0) {
try {
Thread.sleep(500);
} catch (Exception e) {
}
if (service.isSystemReady()) {
return;
}
@BeforeClass
public static void oneTimeInitialization() {
if (Looper.myLooper() == null) {
Looper.prepare();
}
throw new RuntimeException("Wait System Ready timeout");
}
@Before
public void setUp() {
mCountryDetectorService = new CountryDetectorServiceTester(mContext, mHandler);
// Immediately invoke run on the Runnable posted to the handler
doAnswer(invocation -> {
Message message = invocation.getArgument(0);
message.getCallback().run();
return true;
}).when(mHandler).sendMessageAtTime(any(Message.class), anyLong());
}
@Test
public void countryListener_add_successful() throws RemoteException {
CountryListenerTester countryListener = new CountryListenerTester();
mCountryDetectorService.systemRunning();
expect.that(mCountryDetectorService.isListenerSet()).isFalse();
mCountryDetectorService.addCountryListener(countryListener);
expect.that(mCountryDetectorService.isListenerSet()).isTrue();
}
@Test
public void countryListener_remove_successful() throws RemoteException {
CountryListenerTester countryListener = new CountryListenerTester();
mCountryDetectorService.systemRunning();
mCountryDetectorService.addCountryListener(countryListener);
expect.that(mCountryDetectorService.isListenerSet()).isTrue();
mCountryDetectorService.removeCountryListener(countryListener);
expect.that(mCountryDetectorService.isListenerSet()).isFalse();
}
@Test
public void countryListener_notify_successful() throws RemoteException {
CountryListenerTester countryListenerA = new CountryListenerTester();
CountryListenerTester countryListenerB = new CountryListenerTester();
Country country = new Country("US", Country.COUNTRY_SOURCE_NETWORK);
mCountryDetectorService.systemRunning();
mCountryDetectorService.addCountryListener(countryListenerA);
mCountryDetectorService.addCountryListener(countryListenerB);
expect.that(countryListenerA.isNotified()).isFalse();
expect.that(countryListenerB.isNotified()).isFalse();
mCountryDetectorService.notifyReceivers(country);
expect.that(countryListenerA.isNotified()).isTrue();
expect.that(countryListenerB.isNotified()).isTrue();
expect.that(countryListenerA.getCountry().equalsIgnoreSource(country)).isTrue();
expect.that(countryListenerB.getCountry().equalsIgnoreSource(country)).isTrue();
}
}