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

This commit is contained in:
TreeHugger Robot
2020-02-11 23:02:07 +00:00
committed by Android (Google) Code Review
5 changed files with 155 additions and 14 deletions

View File

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

View File

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

View File

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

View File

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

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