Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 275ecd69 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Refactor CountryDetectorService"

parents 55074ee3 50361687
Loading
Loading
Loading
Loading
+34 −42
Original line number Diff line number Diff line
@@ -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,9 +94,15 @@ 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
@@ -154,7 +162,6 @@ public class CountryDetectorService extends ICountryDetector.Stub implements Run
        }
    }


    protected void notifyReceivers(Country country) {
        synchronized (mReceivers) {
            for (Receiver receiver : mReceivers.values()) {
@@ -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;
    }
+1 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ android_test {
        "services.net",
        "services.usage",
        "guava",
        "androidx.test.core",
        "androidx.test.runner",
        "androidx.test.rules",
        "mockito-target-minus-junit4",
+94 −49
Original line number Diff line number Diff line
/*
 * 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;

    @BeforeClass
    public static void oneTimeInitialization() {
        if (Looper.myLooper() == null) {
            Looper.prepare();
        }
    }

    @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());
    }

    public void testNotifyListeners() throws RemoteException {
        CountryDetectorServiceTester serviceTester = new CountryDetectorServiceTester(getContext());
        CountryListenerTester listenerTesterA = new CountryListenerTester();
        CountryListenerTester listenerTesterB = new CountryListenerTester();
    @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);
        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;
            }
        }
        throw new RuntimeException("Wait System Ready timeout");

        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();
    }
}