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

Change-Id: I6fedf6dd00d79b9aeb7b48b91535fe44a936673b
This commit is contained in:
Automerger Merge Worker
2020-02-11 23:05:08 +00:00
5 changed files with 155 additions and 14 deletions

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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() {
mCountryDetector = new ComprehensiveCountryDetector(mContext);
@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());
}

View File

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

View File

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