Add a configuration that prevents HEAD from updating what token responses are
handled. This will have the effect of making the "More" button always go to
network for sessions that branch from HEAD.

PiperOrigin-RevId: 261705035
Change-Id: Ic9b0eaa7c90b91f973568a663b66f4a8d10fb4d7
diff --git a/src/main/java/com/google/android/libraries/feed/api/host/config/Configuration.java b/src/main/java/com/google/android/libraries/feed/api/host/config/Configuration.java
index e82350a..aa46daa 100644
--- a/src/main/java/com/google/android/libraries/feed/api/host/config/Configuration.java
+++ b/src/main/java/com/google/android/libraries/feed/api/host/config/Configuration.java
@@ -66,6 +66,8 @@
     ConfigKey.INITIAL_NON_CACHED_PAGE_SIZE,
     // Only update HEAD and the session making a page request
     ConfigKey.LIMIT_PAGE_UPDATES,
+    // Do not update HEAD when making a page request
+    ConfigKey.LIMIT_PAGE_UPDATES_IN_HEAD,
     // Time in ms that content should be rendered in order to be considered an immediate open
     ConfigKey.LOGGING_IMMEDIATE_CONTENT_THRESHOLD_MS,
     // Boolean which if true, will ask the server for an menu option to launch the interest
@@ -128,6 +130,7 @@
     String FEED_UI_ENABLED = "feed_ui_enabled";
     String INITIAL_NON_CACHED_PAGE_SIZE = "initial_non_cached_page_size";
     String LIMIT_PAGE_UPDATES = "limit_page_updates";
+    String LIMIT_PAGE_UPDATES_IN_HEAD = "limit_page_updates_in_head";
     String LOGGING_IMMEDIATE_CONTENT_THRESHOLD_MS = "logging_immediate_content_threshold_ms";
     String MANAGE_INTERESTS_ENABLED = "manage_interests_enabled";
     String MINIMUM_VALID_ACTION_RATIO = "minimum_valid_action_ratio";
diff --git a/src/main/java/com/google/android/libraries/feed/feedsessionmanager/internal/HeadSessionImpl.java b/src/main/java/com/google/android/libraries/feed/feedsessionmanager/internal/HeadSessionImpl.java
index d602ab9..725e668 100644
--- a/src/main/java/com/google/android/libraries/feed/feedsessionmanager/internal/HeadSessionImpl.java
+++ b/src/main/java/com/google/android/libraries/feed/feedsessionmanager/internal/HeadSessionImpl.java
@@ -40,6 +40,7 @@
   private final TimingUtils timingUtils;
   private final SessionContentTracker sessionContentTracker =
       new SessionContentTracker(/* supportsClearAll= */ true);
+  private final boolean limitPageUpdatesInHead;
 
   private int schemaVersion = 0;
 
@@ -47,9 +48,10 @@
   private int updateCount = 0;
   private int storeMutationFailures = 0;
 
-  HeadSessionImpl(Store store, TimingUtils timingUtils) {
+  HeadSessionImpl(Store store, TimingUtils timingUtils, boolean limitPageUpdatesInHead) {
     this.store = store;
     this.timingUtils = timingUtils;
+    this.limitPageUpdatesInHead = limitPageUpdatesInHead;
   }
 
   /** Initialize head from the stored stream structures. */
@@ -96,6 +98,10 @@
         timeTracker.stop("updateSessionIgnored", getSessionId(), "Token Not Found", contentId);
         Logger.i(TAG, "Token %s not found in session, ignoring update", contentId);
         return;
+      } else if (limitPageUpdatesInHead) {
+        timeTracker.stop("updateSessionIgnored", getSessionId());
+        Logger.i(TAG, "Limited paging updates in HEAD");
+        return;
       }
     }
 
diff --git a/src/main/java/com/google/android/libraries/feed/feedsessionmanager/internal/SessionFactory.java b/src/main/java/com/google/android/libraries/feed/feedsessionmanager/internal/SessionFactory.java
index b3b000e..fac2ee0 100644
--- a/src/main/java/com/google/android/libraries/feed/feedsessionmanager/internal/SessionFactory.java
+++ b/src/main/java/com/google/android/libraries/feed/feedsessionmanager/internal/SessionFactory.java
@@ -32,6 +32,7 @@
   private final ThreadUtils threadUtils;
   private final boolean useTimeScheduler;
   private final boolean limitPagingUpdates;
+  private final boolean limitPagingUpdatesInHead;
 
   public SessionFactory(
       Store store,
@@ -45,6 +46,8 @@
     this.threadUtils = threadUtils;
     useTimeScheduler = config.getValueOrDefault(ConfigKey.USE_TIMEOUT_SCHEDULER, false);
     limitPagingUpdates = config.getValueOrDefault(ConfigKey.LIMIT_PAGE_UPDATES, true);
+    limitPagingUpdatesInHead =
+        config.getValueOrDefault(ConfigKey.LIMIT_PAGE_UPDATES_IN_HEAD, false);
   }
 
   public InitializableSession getSession() {
@@ -54,6 +57,6 @@
   }
 
   public HeadSessionImpl getHeadSession() {
-    return new HeadSessionImpl(store, timingUtils);
+    return new HeadSessionImpl(store, timingUtils, limitPagingUpdatesInHead);
   }
 }
diff --git a/src/test/java/com/google/android/libraries/feed/feedsessionmanager/internal/HeadSessionImplTest.java b/src/test/java/com/google/android/libraries/feed/feedsessionmanager/internal/HeadSessionImplTest.java
index 89b60c6..b5bcb85 100644
--- a/src/test/java/com/google/android/libraries/feed/feedsessionmanager/internal/HeadSessionImplTest.java
+++ b/src/test/java/com/google/android/libraries/feed/feedsessionmanager/internal/HeadSessionImplTest.java
@@ -45,7 +45,8 @@
   private final TimingUtils timingUtils = new TimingUtils();
   private final FakeStore fakeStore =
       new FakeStore(fakeThreadUtils, new FakeTaskQueue(fakeClock, fakeThreadUtils), fakeClock);
-  private final HeadSessionImpl headSession = new HeadSessionImpl(fakeStore, timingUtils);
+  private final HeadSessionImpl headSession =
+      new HeadSessionImpl(fakeStore, timingUtils, /* limitPageUpdatesInHead= */ false);
 
   @Test
   public void testMinimalSessionManager() {
@@ -130,6 +131,29 @@
   }
 
   @Test
+  public void testUpdateFromToken_limitPageUpdatesInHead() {
+    HeadSessionImpl headSession =
+        new HeadSessionImpl(fakeStore, timingUtils, /* limitPageUpdatesInHead= */ true);
+
+    int featureCnt = 3;
+    InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder();
+    addFeatures(protocolBuilder, featureCnt, 1);
+    List<StreamStructure> streamStructures = protocolBuilder.buildAsStreamStructure();
+
+    StreamToken token =
+        StreamToken.newBuilder().setContentId(contentIdGenerators.createTokenContentId(2)).build();
+
+    // The token needs to be in the session so update its content IDs with the token.
+    List<StreamStructure> tokenStructures =
+        new InternalProtocolBuilder().addToken(token.getContentId()).buildAsStreamStructure();
+    headSession.updateSession(false, tokenStructures, SCHEMA_VERSION, null);
+
+    MutationContext context = new MutationContext.Builder().setContinuationToken(token).build();
+    headSession.updateSession(false, streamStructures, SCHEMA_VERSION, context);
+    assertThat(headSession.getContentInSession()).hasSize(1);
+  }
+
+  @Test
   public void testUpdateFromToken_notInSession() {
     int featureCnt = 3;
     InternalProtocolBuilder protocolBuilder = new InternalProtocolBuilder();
@@ -302,7 +326,8 @@
 
   /** Re-read the session from disk and return the set of content. */
   private Set<String> getContentInSession() {
-    HeadSessionImpl headSession = new HeadSessionImpl(fakeStore, timingUtils);
+    HeadSessionImpl headSession =
+        new HeadSessionImpl(fakeStore, timingUtils, /* limitPageUpdatesInHead= */ false);
     headSession.initializeSession(
         fakeStore.getStreamStructures(Store.HEAD_SESSION_ID).getValue(), /* schemaVersion= */ 0);
     return headSession.getContentInSession();