1 // Copyright 2019 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.chrome.browser.customtabs; 6 7 import androidx.annotation.IntDef; 8 import androidx.browser.customtabs.CustomTabsSessionToken; 9 10 import org.chromium.base.metrics.RecordHistogram; 11 import org.chromium.chrome.browser.browserservices.BrowserServicesIntentDataProvider; 12 import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider; 13 import org.chromium.chrome.browser.dependency_injection.ActivityScope; 14 import org.chromium.chrome.browser.gsa.GSAState; 15 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher; 16 import org.chromium.chrome.browser.lifecycle.StartStopWithNativeObserver; 17 import org.chromium.chrome.browser.tab.Tab; 18 import org.chromium.chrome.browser.tab.TabAssociatedApp; 19 20 import java.lang.annotation.Retention; 21 import java.lang.annotation.RetentionPolicy; 22 23 import javax.inject.Inject; 24 25 /** 26 * Keeps the client app alive, when possible, while CustomTabActivity is in foreground (see 27 * {@link CustomTabsConnection#keepAliveForSession}). 28 */ 29 @ActivityScope 30 public class CustomTabActivityClientConnectionKeeper implements StartStopWithNativeObserver { 31 32 @IntDef({ConnectionStatus.DISCONNECTED, ConnectionStatus.DISCONNECTED_KEEP_ALIVE, 33 ConnectionStatus.CONNECTED, ConnectionStatus.CONNECTED_KEEP_ALIVE}) 34 @Retention(RetentionPolicy.SOURCE) 35 private @interface ConnectionStatus { 36 int DISCONNECTED = 0; 37 int DISCONNECTED_KEEP_ALIVE = 1; 38 int CONNECTED = 2; 39 int CONNECTED_KEEP_ALIVE = 3; 40 int NUM_ENTRIES = 4; 41 } 42 43 private final CustomTabsConnection mConnection; 44 private final BrowserServicesIntentDataProvider mIntentDataProvider; 45 private final CustomTabActivityTabProvider mTabProvider; 46 47 private boolean mIsKeepingAlive; 48 49 @Inject CustomTabActivityClientConnectionKeeper(CustomTabsConnection connection, BrowserServicesIntentDataProvider intentDataProvider, ActivityLifecycleDispatcher lifecycleDispatcher, CustomTabActivityTabProvider tabProvider)50 public CustomTabActivityClientConnectionKeeper(CustomTabsConnection connection, 51 BrowserServicesIntentDataProvider intentDataProvider, 52 ActivityLifecycleDispatcher lifecycleDispatcher, 53 CustomTabActivityTabProvider tabProvider) { 54 mConnection = connection; 55 mIntentDataProvider = intentDataProvider; 56 mTabProvider = tabProvider; 57 lifecycleDispatcher.register(this); 58 } 59 60 @Override onStartWithNative()61 public void onStartWithNative() { 62 mIsKeepingAlive = mConnection.keepAliveForSession( 63 mIntentDataProvider.getSession(), mIntentDataProvider.getKeepAliveServiceIntent()); 64 } 65 66 @Override onStopWithNative()67 public void onStopWithNative() { 68 mConnection.dontKeepAliveForSession(mIntentDataProvider.getSession()); 69 mIsKeepingAlive = false; 70 } 71 72 /** 73 * Records current client connection status. 74 */ recordClientConnectionStatus()75 public void recordClientConnectionStatus() { 76 Tab tab = mTabProvider.getTab(); 77 String packageName = tab == null ? null : TabAssociatedApp.getAppId(tab); 78 if (packageName == null) return; // No associated package 79 80 CustomTabsSessionToken session = mIntentDataProvider.getSession(); 81 boolean isConnected = 82 packageName.equals(mConnection.getClientPackageNameForSession(session)); 83 int status = -1; 84 if (isConnected) { 85 if (mIsKeepingAlive) { 86 status = ConnectionStatus.CONNECTED_KEEP_ALIVE; 87 } else { 88 status = ConnectionStatus.CONNECTED; 89 } 90 } else { 91 if (mIsKeepingAlive) { 92 status = ConnectionStatus.DISCONNECTED_KEEP_ALIVE; 93 } else { 94 status = ConnectionStatus.DISCONNECTED; 95 } 96 } 97 assert status >= 0; 98 99 if (GSAState.isGsaPackageName(packageName)) { 100 RecordHistogram.recordEnumeratedHistogram("CustomTabs.ConnectionStatusOnReturn.GSA", 101 status, ConnectionStatus.NUM_ENTRIES); 102 } else { 103 RecordHistogram.recordEnumeratedHistogram("CustomTabs.ConnectionStatusOnReturn.NonGSA", 104 status, ConnectionStatus.NUM_ENTRIES); 105 } 106 } 107 } 108