Avoid creating multiple window contexts

Reuse the previous window context if it hasn't been garbage collected.
This ensures that the RotaryService doesn't create more than one window
context when it's enabled and disabled repeatedly. Android imposes a
limit on the number of window contexts without a corresponding surface.

Test: enable and disable RotaryService 6 times via KitchenSink
Bug: 173435210
Change-Id: I1928a1da65013c0e850b609b6d01f388a4c891fd
diff --git a/src/com/android/car/rotary/RotaryService.java b/src/com/android/car/rotary/RotaryService.java
index 6a89305..42600c4 100644
--- a/src/com/android/car/rotary/RotaryService.java
+++ b/src/com/android/car/rotary/RotaryService.java
@@ -88,6 +88,7 @@
 
 import com.android.car.ui.utils.DirectManipulationHelper;
 
+import java.lang.ref.WeakReference;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -139,6 +140,14 @@
     private static final String SHARED_PREFS = "com.android.car.rotary.RotaryService";
     private static final String TOUCH_INPUT_METHOD_PREFIX = "TOUCH_INPUT_METHOD_";
 
+    /**
+     * A reference to {@link #mWindowContext} or null if one hasn't been created. This is static in
+     * order to prevent the creation of multiple window contexts when this service is enabled and
+     * disabled repeatedly. Android imposes a limit on the number of window contexts without a
+     * corresponding surface.
+     */
+    @Nullable private static WeakReference<Context> sWindowContext;
+
     @NonNull
     private NodeCopier mNodeCopier = new NodeCopier();
 
@@ -296,7 +305,11 @@
      */
     private int mCenterButtonRepeatCount;
 
-    private Context mWindowContext;
+    /**
+     * A context to use for fetching the {@link WindowManager} and creating the touch overlay or
+     * null if one hasn't been created yet.
+     */
+    @Nullable private Context mWindowContext;
 
     private static final Map<Integer, Integer> TEST_TO_REAL_KEYCODE_MAP;
 
@@ -572,13 +585,20 @@
     }
 
     private Context getWindowContext() {
+        if (mWindowContext == null && sWindowContext != null) {
+            mWindowContext = sWindowContext.get();
+            if (mWindowContext != null) {
+                L.d("Reusing window context");
+            }
+        }
         if (mWindowContext == null) {
             // We need to set the display before creating the WindowContext.
             DisplayManager displayManager = getSystemService(DisplayManager.class);
             Display primaryDisplay = displayManager.getDisplay(DEFAULT_DISPLAY);
             updateDisplay(primaryDisplay.getDisplayId());
-
+            L.d("Creating window context");
             mWindowContext = createWindowContext(TYPE_APPLICATION_OVERLAY, null);
+            sWindowContext = new WeakReference<>(mWindowContext);
         }
         return mWindowContext;
     }