By implementing this interface and overriding the + * {@code config_deviceSpecificDeviceStatePolicyProvider}, a device-specific implementations + * of {@link DeviceStatePolicy} can be supplied. + */ + public interface Provider { + /** + * Instantiates a new {@link DeviceStatePolicy}. + * + * @see DeviceStatePolicy#DeviceStatePolicy + */ + DeviceStatePolicy instantiate(@NonNull Context context); + + /** + * Instantiates the device-specific {@link DeviceStatePolicy.Provider}. + * + * Checks the {@code config_deviceSpecificDeviceStatePolicyProvider} resource to see if + * a device specific policy provider has been supplied. If so, returns an instance of that + * provider. If there is no value provided then the method returns the + * {@link DeviceStatePolicy.DefaultProvider}. + * + * An {@link IllegalStateException} is thrown if there is a value provided for that + * resource, but it doesn't correspond to a class that is found. + */ + static Provider fromResources(@NonNull Resources res) { + final String name = res.getString( + com.android.internal.R.string.config_deviceSpecificDeviceStatePolicyProvider); + if (TextUtils.isEmpty(name)) { + return new DeviceStatePolicy.DefaultProvider(); + } + + try { + return (DeviceStatePolicy.Provider) Class.forName(name).newInstance(); + } catch (ReflectiveOperationException | ClassCastException e) { + throw new IllegalStateException("Couldn't instantiate class " + name + + " for config_deviceSpecificDeviceStatePolicyProvider:" + + " make sure it has a public zero-argument constructor" + + " and implements DeviceStatePolicy.Provider", e); + } + } + } + } diff --git a/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java b/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java index 154f9a455a1a5..7754944c008c9 100644 --- a/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java +++ b/services/core/java/com/android/server/policy/DeviceStatePolicyImpl.java @@ -27,12 +27,11 @@ import com.android.server.devicestate.DeviceStateProvider; * * @see DeviceStateProviderImpl */ -public final class DeviceStatePolicyImpl implements DeviceStatePolicy { - private final Context mContext; +public final class DeviceStatePolicyImpl extends DeviceStatePolicy { private final DeviceStateProvider mProvider; - public DeviceStatePolicyImpl(Context context) { - mContext = context; + public DeviceStatePolicyImpl(@NonNull Context context) { + super(context); mProvider = DeviceStateProviderImpl.create(mContext); } diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java index 03eba9bfc35c2..d2cff0ea1968c 100644 --- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java @@ -551,13 +551,14 @@ public final class DeviceStateManagerServiceTest { Assert.assertTrue(Arrays.equals(expected, actual)); } - private static final class TestDeviceStatePolicy implements DeviceStatePolicy { + private static final class TestDeviceStatePolicy extends DeviceStatePolicy { private final DeviceStateProvider mProvider; private int mLastDeviceStateRequestedToConfigure = INVALID_DEVICE_STATE; private boolean mConfigureBlocked = false; private Runnable mPendingConfigureCompleteRunnable; TestDeviceStatePolicy(DeviceStateProvider provider) { + super(InstrumentationRegistry.getContext()); mProvider = provider; } diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStatePolicyProviderTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStatePolicyProviderTest.java new file mode 100644 index 0000000000000..0bd81b78ac977 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStatePolicyProviderTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2021 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.devicestate; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertThrows; + +import android.content.Context; +import android.content.res.Resources; +import android.platform.test.annotations.Presubmit; + +import org.hamcrest.Matchers; +import org.junit.Assert; +import org.junit.Test; + +/** + * Unit tests for the {@link DeviceStatePolicy.Provider} + *
+ * Build/Install/Run: + *atest DeviceStatePolicyProviderTest
+ */
+@Presubmit
+public class DeviceStatePolicyProviderTest {
+
+ @Test
+ public void test_emptyPolicyProvider() {
+ Assert.assertThat(DeviceStatePolicy.Provider.fromResources(resourcesWithProvider("")),
+ Matchers.instanceOf(DeviceStatePolicy.DefaultProvider.class));
+ }
+
+ @Test
+ public void test_nullPolicyProvider() {
+ Assert.assertThat(DeviceStatePolicy.Provider.fromResources(resourcesWithProvider(null)),
+ Matchers.instanceOf(DeviceStatePolicy.DefaultProvider.class));
+ }
+
+ @Test
+ public void test_customPolicyProvider() {
+ Assert.assertThat(DeviceStatePolicy.Provider.fromResources(resourcesWithProvider(
+ TestProvider.class.getName())),
+ Matchers.instanceOf(TestProvider.class));
+ }
+
+ @Test
+ public void test_badPolicyProvider_notImplementingProviderInterface() {
+ assertThrows(IllegalStateException.class, () -> {
+ DeviceStatePolicy.Provider.fromResources(resourcesWithProvider(
+ Object.class.getName()));
+ });
+ }
+
+ @Test
+ public void test_badPolicyProvider_doesntExist() {
+ assertThrows(IllegalStateException.class, () -> {
+ DeviceStatePolicy.Provider.fromResources(resourcesWithProvider(
+ "com.android.devicestate.nonexistent.policy"));
+ });
+ }
+
+ private static Resources resourcesWithProvider(String provider) {
+ final Resources mockResources = mock(Resources.class);
+ when(mockResources.getString(
+ com.android.internal.R.string.config_deviceSpecificDeviceStatePolicyProvider))
+ .thenReturn(provider);
+ return mockResources;
+ }
+
+ // Stub implementation of DeviceStatePolicy.Provider for testing
+ static class TestProvider implements DeviceStatePolicy.Provider {
+ @Override
+ public DeviceStatePolicy instantiate(Context context) {
+ throw new RuntimeException("test stub");
+ }
+ }
+}