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

Commit 88fffb6a authored by Automerger Merge Worker's avatar Automerger Merge Worker
Browse files

Merge "CountryDetector: Enable detector class override for automotive" into...

Merge "CountryDetector: Enable detector class override for automotive" into qt-qpr1-dev am: 6ac8c0d0

Change-Id: I6fedf6dd00d79b9aeb7b48b91535fe44a936673b
parents fcc25eb5 6ac8c0d0
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -4349,7 +4349,11 @@
    <!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing
         check after reboot or airplane mode toggling -->
    <bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">false</bool>

    <!-- Boolean indicating that the system will use autoSuspend. If set to false, autoSuspend
         is not used and the system will only suspend upon an explicit request. -->
    <bool translatable="false" name="config_enableAutoSuspend">true</bool>

    <!-- Class name of the custom country detector to be used. -->
    <string name="config_customCountryDetector" translatable="false">com.android.server.location.ComprehensiveCountryDetector</string>
</resources>
+2 −0
Original line number Diff line number Diff line
@@ -3697,6 +3697,8 @@

  <java-symbol type="bool" name="config_maskMainBuiltInDisplayCutout" />

  <java-symbol type="string" name="config_customCountryDetector" />

  <!-- For Foldables -->
  <java-symbol type="bool" name="config_lidControlsDisplayFold" />
  <java-symbol type="string" name="config_foldedArea" />
+45 −5
Original line number Diff line number Diff line
@@ -24,21 +24,29 @@ import android.location.ICountryListener;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.Slog;

import com.android.internal.R;
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 com.android.server.location.CountryDetectorBase;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
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. The default country detection is made through
 * {@link com.android.server.location.ComprehensiveCountryDetector}. It is possible to overlay the
 * detection algorithm by overlaying the attribute R.string.config_customCountryDetector with the
 * custom class name to use instead. The custom class must extend
 * {@link com.android.server.location.CountryDetectorBase}
 *
 * @hide
 */
@@ -88,7 +96,7 @@ public class CountryDetectorService extends ICountryDetector.Stub {

    private final HashMap<IBinder, Receiver> mReceivers;
    private final Context mContext;
    private ComprehensiveCountryDetector mCountryDetector;
    private CountryDetectorBase mCountryDetector;
    private boolean mSystemReady;
    private Handler mHandler;
    private CountryListener mLocationBasedDetectorListener;
@@ -184,8 +192,17 @@ public class CountryDetectorService extends ICountryDetector.Stub {
                });
    }

    private void initialize() {
    @VisibleForTesting
    void initialize() {
        final String customCountryClass = mContext.getString(R.string.config_customCountryDetector);
        if (!TextUtils.isEmpty(customCountryClass)) {
            mCountryDetector = loadCustomCountryDetectorIfAvailable(customCountryClass);
        }

        if (mCountryDetector == null) {
            Slog.d(TAG, "Using default country detector");
            mCountryDetector = new ComprehensiveCountryDetector(mContext);
        }
        mLocationBasedDetectorListener = country -> mHandler.post(() -> notifyReceivers(country));
    }

@@ -193,11 +210,33 @@ public class CountryDetectorService extends ICountryDetector.Stub {
        mHandler.post(() -> mCountryDetector.setCountryListener(listener));
    }

    @VisibleForTesting
    CountryDetectorBase getCountryDetector() {
        return mCountryDetector;
    }

    @VisibleForTesting
    boolean isSystemReady() {
        return mSystemReady;
    }

    private CountryDetectorBase loadCustomCountryDetectorIfAvailable(
            final String customCountryClass) {
        CountryDetectorBase customCountryDetector = null;

        Slog.d(TAG, "Using custom country detector class: " + customCountryClass);
        try {
            customCountryDetector = Class.forName(customCountryClass).asSubclass(
                    CountryDetectorBase.class).getConstructor(Context.class).newInstance(
                    mContext);
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
                | NoSuchMethodException | InvocationTargetException e) {
            Slog.e(TAG, "Could not instantiate the custom country detector class");
        }

        return customCountryDetector;
    }

    @SuppressWarnings("unused")
    @Override
    protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
@@ -206,9 +245,10 @@ public class CountryDetectorService extends ICountryDetector.Stub {
        try {
            final Printer p = new PrintWriterPrinter(fout);
            p.println("CountryDetectorService state:");
            p.println("Country detector class=" + mCountryDetector.getClass().getName());
            p.println("  Number of listeners=" + mReceivers.keySet().size());
            if (mCountryDetector == null) {
                p.println("  ComprehensiveCountryDetector not initialized");
                p.println("  CountryDetector not initialized");
            } else {
                p.println("  " + mCountryDetector.toString());
            }
+68 −9
Original line number Diff line number Diff line
@@ -19,8 +19,11 @@ package com.android.server;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;

import android.content.Context;
import android.content.res.Resources;
import android.location.Country;
import android.location.CountryListener;
import android.location.ICountryListener;
@@ -31,6 +34,10 @@ import android.os.RemoteException;

import androidx.test.core.app.ApplicationProvider;

import com.android.internal.R;
import com.android.server.location.ComprehensiveCountryDetector;
import com.android.server.location.CustomCountryDetectorTestClass;

import com.google.common.truth.Expect;

import org.junit.Before;
@@ -38,12 +45,18 @@ import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class CountryDetectorServiceTest {

    private static final String VALID_CUSTOM_TEST_CLASS =
            "com.android.server.location.CustomCountryDetectorTestClass";
    private static final String INVALID_CUSTOM_TEST_CLASS =
            "com.android.server.location.MissingCountryDetectorTestClass";

    private static class CountryListenerTester extends ICountryListener.Stub {
        private Country mCountry;

@@ -83,12 +96,11 @@ public class CountryDetectorServiceTest {
        }
    }

    @Rule
    public final Expect expect = Expect.create();
    @Spy
    private Context mContext = ApplicationProvider.getApplicationContext();
    @Spy
    private Handler mHandler = new Handler(Looper.myLooper());
    @Rule public final Expect expect = Expect.create();
    @Spy private Context mContext = ApplicationProvider.getApplicationContext();
    @Spy private Handler mHandler = new Handler(Looper.myLooper());
    @Mock private Resources mResources;

    private CountryDetectorServiceTester mCountryDetectorService;

    @BeforeClass
@@ -108,10 +120,12 @@ public class CountryDetectorServiceTest {
            message.getCallback().run();
            return true;
        }).when(mHandler).sendMessageAtTime(any(Message.class), anyLong());

        doReturn(mResources).when(mContext).getResources();
    }

    @Test
    public void countryListener_add_successful() throws RemoteException {
    public void addCountryListener_validListener_listenerAdded() throws RemoteException {
        CountryListenerTester countryListener = new CountryListenerTester();

        mCountryDetectorService.systemRunning();
@@ -122,7 +136,7 @@ public class CountryDetectorServiceTest {
    }

    @Test
    public void countryListener_remove_successful() throws RemoteException {
    public void removeCountryListener_validListener_listenerRemoved() throws RemoteException {
        CountryListenerTester countryListener = new CountryListenerTester();

        mCountryDetectorService.systemRunning();
@@ -133,8 +147,31 @@ public class CountryDetectorServiceTest {
        expect.that(mCountryDetectorService.isListenerSet()).isFalse();
    }

    @Test(expected = RemoteException.class)
    public void addCountryListener_serviceNotReady_throwsException() throws RemoteException {
        CountryListenerTester countryListener = new CountryListenerTester();

        expect.that(mCountryDetectorService.isSystemReady()).isFalse();
        mCountryDetectorService.addCountryListener(countryListener);
    }

    @Test(expected = RemoteException.class)
    public void removeCountryListener_serviceNotReady_throwsException() throws RemoteException {
        CountryListenerTester countryListener = new CountryListenerTester();

        expect.that(mCountryDetectorService.isSystemReady()).isFalse();
        mCountryDetectorService.removeCountryListener(countryListener);
    }

    @Test
    public void countryListener_notify_successful() throws RemoteException {
    public void detectCountry_serviceNotReady_returnNull() {
        expect.that(mCountryDetectorService.isSystemReady()).isFalse();

        expect.that(mCountryDetectorService.detectCountry()).isNull();
    }

    @Test
    public void notifyReceivers_twoListenersRegistered_bothNotified() throws RemoteException {
        CountryListenerTester countryListenerA = new CountryListenerTester();
        CountryListenerTester countryListenerB = new CountryListenerTester();
        Country country = new Country("US", Country.COUNTRY_SOURCE_NETWORK);
@@ -151,4 +188,26 @@ public class CountryDetectorServiceTest {
        expect.that(countryListenerA.getCountry().equalsIgnoreSource(country)).isTrue();
        expect.that(countryListenerB.getCountry().equalsIgnoreSource(country)).isTrue();
    }

    @Test
    public void initialize_deviceWithCustomDetector_useCustomDetectorClass() {
        when(mResources.getString(R.string.config_customCountryDetector))
                .thenReturn(VALID_CUSTOM_TEST_CLASS);

        mCountryDetectorService.initialize();

        expect.that(mCountryDetectorService.getCountryDetector())
                .isInstanceOf(CustomCountryDetectorTestClass.class);
    }

    @Test
    public void initialize_deviceWithInvalidCustomDetector_useDefaultDetector() {
        when(mResources.getString(R.string.config_customCountryDetector))
                .thenReturn(INVALID_CUSTOM_TEST_CLASS);

        mCountryDetectorService.initialize();

        expect.that(mCountryDetectorService.getCountryDetector())
                .isInstanceOf(ComprehensiveCountryDetector.class);
    }
}
+36 −0
Original line number Diff line number Diff line
/*
 * 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.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * 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.
 */

package com.android.server.location;

import android.content.Context;
import android.location.Country;

public class CustomCountryDetectorTestClass extends CountryDetectorBase {
    public CustomCountryDetectorTestClass(Context ctx) {
        super(ctx);
    }

    @Override
    public Country detectCountry() {
        return null;
    }

    @Override
    public void stop() {

    }
}