Merge "CountryDetector: Enable detector class override for automotive" into qt-qpr1-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
6ac8c0d096
@@ -4273,7 +4273,11 @@
|
|||||||
<!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing
|
<!-- Boolean indicating whether frameworks needs to reset cell broadcast geo-fencing
|
||||||
check after reboot or airplane mode toggling -->
|
check after reboot or airplane mode toggling -->
|
||||||
<bool translatable="false" name="reset_geo_fencing_check_after_boot_or_apm">false</bool>
|
<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
|
<!-- 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. -->
|
is not used and the system will only suspend upon an explicit request. -->
|
||||||
<bool translatable="false" name="config_enableAutoSuspend">true</bool>
|
<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>
|
</resources>
|
||||||
|
|||||||
@@ -3689,6 +3689,8 @@
|
|||||||
|
|
||||||
<java-symbol type="bool" name="config_maskMainBuiltInDisplayCutout" />
|
<java-symbol type="bool" name="config_maskMainBuiltInDisplayCutout" />
|
||||||
|
|
||||||
|
<java-symbol type="string" name="config_customCountryDetector" />
|
||||||
|
|
||||||
<!-- For Foldables -->
|
<!-- For Foldables -->
|
||||||
<java-symbol type="bool" name="config_lidControlsDisplayFold" />
|
<java-symbol type="bool" name="config_lidControlsDisplayFold" />
|
||||||
<java-symbol type="string" name="config_foldedArea" />
|
<java-symbol type="string" name="config_foldedArea" />
|
||||||
|
|||||||
@@ -24,21 +24,29 @@ import android.location.ICountryListener;
|
|||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.PrintWriterPrinter;
|
import android.util.PrintWriterPrinter;
|
||||||
import android.util.Printer;
|
import android.util.Printer;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
|
import com.android.internal.R;
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.os.BackgroundThread;
|
import com.android.internal.os.BackgroundThread;
|
||||||
import com.android.internal.util.DumpUtils;
|
import com.android.internal.util.DumpUtils;
|
||||||
import com.android.server.location.ComprehensiveCountryDetector;
|
import com.android.server.location.ComprehensiveCountryDetector;
|
||||||
|
import com.android.server.location.CountryDetectorBase;
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.HashMap;
|
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
|
* @hide
|
||||||
*/
|
*/
|
||||||
@@ -88,7 +96,7 @@ public class CountryDetectorService extends ICountryDetector.Stub {
|
|||||||
|
|
||||||
private final HashMap<IBinder, Receiver> mReceivers;
|
private final HashMap<IBinder, Receiver> mReceivers;
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private ComprehensiveCountryDetector mCountryDetector;
|
private CountryDetectorBase mCountryDetector;
|
||||||
private boolean mSystemReady;
|
private boolean mSystemReady;
|
||||||
private Handler mHandler;
|
private Handler mHandler;
|
||||||
private CountryListener mLocationBasedDetectorListener;
|
private CountryListener mLocationBasedDetectorListener;
|
||||||
@@ -184,8 +192,17 @@ public class CountryDetectorService extends ICountryDetector.Stub {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initialize() {
|
@VisibleForTesting
|
||||||
mCountryDetector = new ComprehensiveCountryDetector(mContext);
|
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));
|
mLocationBasedDetectorListener = country -> mHandler.post(() -> notifyReceivers(country));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,11 +210,33 @@ public class CountryDetectorService extends ICountryDetector.Stub {
|
|||||||
mHandler.post(() -> mCountryDetector.setCountryListener(listener));
|
mHandler.post(() -> mCountryDetector.setCountryListener(listener));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
CountryDetectorBase getCountryDetector() {
|
||||||
|
return mCountryDetector;
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
boolean isSystemReady() {
|
boolean isSystemReady() {
|
||||||
return mSystemReady;
|
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")
|
@SuppressWarnings("unused")
|
||||||
@Override
|
@Override
|
||||||
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
|
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
|
||||||
@@ -206,9 +245,10 @@ public class CountryDetectorService extends ICountryDetector.Stub {
|
|||||||
try {
|
try {
|
||||||
final Printer p = new PrintWriterPrinter(fout);
|
final Printer p = new PrintWriterPrinter(fout);
|
||||||
p.println("CountryDetectorService state:");
|
p.println("CountryDetectorService state:");
|
||||||
|
p.println("Country detector class=" + mCountryDetector.getClass().getName());
|
||||||
p.println(" Number of listeners=" + mReceivers.keySet().size());
|
p.println(" Number of listeners=" + mReceivers.keySet().size());
|
||||||
if (mCountryDetector == null) {
|
if (mCountryDetector == null) {
|
||||||
p.println(" ComprehensiveCountryDetector not initialized");
|
p.println(" CountryDetector not initialized");
|
||||||
} else {
|
} else {
|
||||||
p.println(" " + mCountryDetector.toString());
|
p.println(" " + mCountryDetector.toString());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,11 @@ package com.android.server;
|
|||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyLong;
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
import static org.mockito.Mockito.doAnswer;
|
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.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
import android.location.Country;
|
import android.location.Country;
|
||||||
import android.location.CountryListener;
|
import android.location.CountryListener;
|
||||||
import android.location.ICountryListener;
|
import android.location.ICountryListener;
|
||||||
@@ -31,6 +34,10 @@ import android.os.RemoteException;
|
|||||||
|
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
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 com.google.common.truth.Expect;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -38,12 +45,18 @@ import org.junit.BeforeClass;
|
|||||||
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.Mock;
|
||||||
import org.mockito.Spy;
|
import org.mockito.Spy;
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
public class CountryDetectorServiceTest {
|
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 static class CountryListenerTester extends ICountryListener.Stub {
|
||||||
private Country mCountry;
|
private Country mCountry;
|
||||||
|
|
||||||
@@ -83,12 +96,11 @@ public class CountryDetectorServiceTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Rule
|
@Rule public final Expect expect = Expect.create();
|
||||||
public final Expect expect = Expect.create();
|
@Spy private Context mContext = ApplicationProvider.getApplicationContext();
|
||||||
@Spy
|
@Spy private Handler mHandler = new Handler(Looper.myLooper());
|
||||||
private Context mContext = ApplicationProvider.getApplicationContext();
|
@Mock private Resources mResources;
|
||||||
@Spy
|
|
||||||
private Handler mHandler = new Handler(Looper.myLooper());
|
|
||||||
private CountryDetectorServiceTester mCountryDetectorService;
|
private CountryDetectorServiceTester mCountryDetectorService;
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
@@ -108,10 +120,12 @@ public class CountryDetectorServiceTest {
|
|||||||
message.getCallback().run();
|
message.getCallback().run();
|
||||||
return true;
|
return true;
|
||||||
}).when(mHandler).sendMessageAtTime(any(Message.class), anyLong());
|
}).when(mHandler).sendMessageAtTime(any(Message.class), anyLong());
|
||||||
|
|
||||||
|
doReturn(mResources).when(mContext).getResources();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void countryListener_add_successful() throws RemoteException {
|
public void addCountryListener_validListener_listenerAdded() throws RemoteException {
|
||||||
CountryListenerTester countryListener = new CountryListenerTester();
|
CountryListenerTester countryListener = new CountryListenerTester();
|
||||||
|
|
||||||
mCountryDetectorService.systemRunning();
|
mCountryDetectorService.systemRunning();
|
||||||
@@ -122,7 +136,7 @@ public class CountryDetectorServiceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void countryListener_remove_successful() throws RemoteException {
|
public void removeCountryListener_validListener_listenerRemoved() throws RemoteException {
|
||||||
CountryListenerTester countryListener = new CountryListenerTester();
|
CountryListenerTester countryListener = new CountryListenerTester();
|
||||||
|
|
||||||
mCountryDetectorService.systemRunning();
|
mCountryDetectorService.systemRunning();
|
||||||
@@ -133,8 +147,31 @@ public class CountryDetectorServiceTest {
|
|||||||
expect.that(mCountryDetectorService.isListenerSet()).isFalse();
|
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
|
@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 countryListenerA = new CountryListenerTester();
|
||||||
CountryListenerTester countryListenerB = new CountryListenerTester();
|
CountryListenerTester countryListenerB = new CountryListenerTester();
|
||||||
Country country = new Country("US", Country.COUNTRY_SOURCE_NETWORK);
|
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(countryListenerA.getCountry().equalsIgnoreSource(country)).isTrue();
|
||||||
expect.that(countryListenerB.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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* 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() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user