diff --git a/termux/termux-app/src/main/java/com/termux/app/TermuxService.java b/termux/termux-app/src/main/java/com/termux/app/TermuxService.java index 628242a677..0ad293f617 100644 --- a/termux/termux-app/src/main/java/com/termux/app/TermuxService.java +++ b/termux/termux-app/src/main/java/com/termux/app/TermuxService.java @@ -118,9 +118,7 @@ public void close() { public void onCreate() { Logger.logVerbose(LOG_TAG, "onCreate"); - // Get Termux app SharedProperties without loading from disk since TermuxApplication handles - // load and TermuxActivity handles reloads - mProperties = TermuxAppSharedProperties.getProperties(); + mProperties = TermuxAppSharedProperties.init(getApplicationContext()); // Use init() instead of getShellManager() so the singleton is lazily created if the // OS auto-restarted the service after process death (TermuxApplication.onCreate did not diff --git a/termux/termux-app/src/test/java/com/termux/app/TermuxServicePropertiesNpeTest.java b/termux/termux-app/src/test/java/com/termux/app/TermuxServicePropertiesNpeTest.java new file mode 100644 index 0000000000..18fc079bbf --- /dev/null +++ b/termux/termux-app/src/test/java/com/termux/app/TermuxServicePropertiesNpeTest.java @@ -0,0 +1,58 @@ +package com.termux.app; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +import com.termux.shared.termux.settings.properties.TermuxAppSharedProperties; + +import java.lang.reflect.Field; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.android.controller.ServiceController; + +@RunWith(RobolectricTestRunner.class) +public class TermuxServicePropertiesNpeTest { + + @Before + public void clearPropertiesSingleton() throws Exception { + Field singleton = TermuxAppSharedProperties.class.getDeclaredField("properties"); + singleton.setAccessible(true); + singleton.set(null, null); + } + + @Test + public void onCreateBeforeTheAppLoaderRan_initializesTheServiceProperties() throws Exception { + assertNull(TermuxAppSharedProperties.getProperties()); + + ServiceController controller = Robolectric.buildService(TermuxService.class).create(); + + Object serviceProperties = servicePropertiesOf(controller.get()); + assertNotNull("TermuxService.mProperties must not be null after onCreate()", serviceProperties); + assertSame(TermuxAppSharedProperties.getProperties(), serviceProperties); + + controller.destroy(); + } + + @Test + public void onCreateAfterTheAppLoaderRan_reusesTheLoadedProperties() throws Exception { + TermuxAppSharedProperties loaded = TermuxAppSharedProperties.init(RuntimeEnvironment.getApplication()); + + ServiceController controller = Robolectric.buildService(TermuxService.class).create(); + + assertSame(loaded, servicePropertiesOf(controller.get())); + + controller.destroy(); + } + + private static Object servicePropertiesOf(TermuxService service) throws Exception { + Field field = TermuxService.class.getDeclaredField("mProperties"); + field.setAccessible(true); + return field.get(service); + } +} diff --git a/termux/termux-shared/src/main/java/com/termux/shared/termux/settings/properties/TermuxAppSharedProperties.java b/termux/termux-shared/src/main/java/com/termux/shared/termux/settings/properties/TermuxAppSharedProperties.java index 7faf5f8b10..63d6f2b815 100644 --- a/termux/termux-shared/src/main/java/com/termux/shared/termux/settings/properties/TermuxAppSharedProperties.java +++ b/termux/termux-shared/src/main/java/com/termux/shared/termux/settings/properties/TermuxAppSharedProperties.java @@ -8,7 +8,7 @@ public class TermuxAppSharedProperties extends TermuxSharedProperties { - private static TermuxAppSharedProperties properties; + private static volatile TermuxAppSharedProperties properties; private TermuxAppSharedProperties(@NonNull Context context) { @@ -23,7 +23,7 @@ private TermuxAppSharedProperties(@NonNull Context context) { * @param context The {@link Context} for operations. * @return Returns the {@link TermuxAppSharedProperties}. */ - public static TermuxAppSharedProperties init(@NonNull Context context) { + public static synchronized TermuxAppSharedProperties init(@NonNull Context context) { if (properties == null) properties = new TermuxAppSharedProperties(context);