Commit f0077b70 authored by Peter E Conn's avatar Peter E Conn Committed by Commit Bot

🤝 Notice when TWA client app is uninstalled.

This change adds code to react when a Trusted Web Activity client is
uninstalled or has its data wiped. Currently it does nothing on this
trigger, that will come in a later CL.

Bug: 888447
Change-Id: I354e5d0ade9327659f198e9ba099875daf89f378
Reviewed-on: https://chromium-review.googlesource.com/c/1243485
Commit-Queue: Peter Conn <peconn@chromium.org>
Reviewed-by: default avatarYusuf Ozuysal <yusufo@chromium.org>
Reviewed-by: default avatarPeter Beverloo <peter@chromium.org>
Cr-Commit-Position: refs/heads/master@{#597031}
parent 36f488ca
...@@ -794,6 +794,16 @@ by a child template that "extends" this file. ...@@ -794,6 +794,16 @@ by a child template that "extends" this file.
android:exported="false"> android:exported="false">
</service> </service>
<!-- Components for Trusted Web Activities -->
<receiver android:name="org.chromium.chrome.browser.browserservices.ClearDataBroadcastReceiver"
android:exported="true">
<intent-filter>
<data android:scheme="package" />
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
<action android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
</intent-filter>
</receiver>
<!-- Service for decoding images in a sandboxed process. --> <!-- Service for decoding images in a sandboxed process. -->
<service <service
android:description="@string/decoder_description" android:description="@string/decoder_description"
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.browserservices;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import org.chromium.base.Log;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* A {@link android.content.BroadcastReceiver} that detects when a Trusted Web Activity client app
* has been uninstalled or has had its data cleared. When this happens we clear Chrome's data
* corresponding to that app.
*
* Trusted Web Activities are registered to an origin (eg https://www.example.com), however because
* cookies can be scoped more loosely, at TLD+1 level (eg *.example.com) [1], we need to clear data
* at that level. This unfortunately can lead to too much data getting cleared - for example if the
* https://peconn.github.io TWA is cleared, you'll loose cookies for https://beverloo.github.io too.
*
* We find this acceptable for two reasons:
* - The alternative is *not* clearing some related data - eg a TWA linked to
* https://peconn.github.io sets a cookie with Domain=github.io. The TWA is uninstalled and
* reinstalled and it can access the cookie it stored before.
* - We ask the user before clearing the data and while doing so display the scope of data we're
* going to wipe.
*
* [1] https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#Scope_of_cookies
*/
public class ClearDataBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "ClearDataBroadRec";
private static final Set<String> BROADCASTS = new HashSet<>(Arrays.asList(
Intent.ACTION_PACKAGE_DATA_CLEARED,
Intent.ACTION_PACKAGE_FULLY_REMOVED
));
@Override
public void onReceive(Context context, Intent intent) {
if (intent == null) return;
// Since we only care about ACTION_PACKAGE_DATA_CLEARED and and ACTION_PACKAGE_FULLY_REMOVED
// which are protected Intents, we can assume that anything that gets past here will be a
// legitimate Intent sent by the system.
if (!BROADCASTS.contains(intent.getAction())) return;
int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
if (uid == -1) return;
// TODO(peconn): Add UMA to record time taken.
ClientAppDataRegister register = new ClientAppDataRegister();
if (!register.chromeHoldsDataForPackage(uid)) return;
String appName = register.getAppNameForRegisteredUid(uid);
Set<String> origins = register.getOriginsForRegisteredUid(uid);
for (String origin : origins) {
String action = Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(intent.getAction())
? "been uninstalled" : "had its data cleared";
Log.d(TAG, "%s has just %s, it was linked to %s.", appName, action, origin);
}
}
}
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.browserservices;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.text.TextUtils;
import org.chromium.base.Log;
import java.util.HashSet;
import java.util.Set;
/**
* Takes care of recording that Chrome contains data for the client app in the
* {@link ClientAppDataRegister}. It performs two main duties:
* - Holds a cache to deduplicate requests (for performance not correctness).
* - Transforming the package name into a uid and app label.
*
* Lifecycle: There should be a 1-1 relationship between this class and
* {@link TrustedWebActivityUi}. Having more instances won't effect correctness, but will limit the
* performance benefits of the cache.
* Thread safety: All methods on this class should be called from the same thread.
*/
public class ClientAppDataRecorder {
private static final String TAG = "TWAClientAppData";
private final PackageManager mPackageManager;
/** Underlying data register. */
private final ClientAppDataRegister mClientAppDataRegister;
/**
* Cache so we don't send the same request multiple times. {@link #register} is called on each
* navigation and each call to {@link ClientAppDataRegister#registerPackageForOrigin} modifies
* SharedPreferences, so we need to cut down on the number of calls.
*/
private final Set<String> mCache = new HashSet<>();
public ClientAppDataRecorder(PackageManager packageManager,
ClientAppDataRegister clientAppDataRegister) {
mPackageManager = packageManager;
mClientAppDataRegister = clientAppDataRegister;
}
/**
* Calls {@link ClientAppDataRegister#registerPackageForOrigin}, looking up the uid and
* app name for the |packageName| and deduplicating multiple requests with the same parameters.
*/
/* package */ void register(String packageName, Origin origin) {
if (mCache.contains(combine(packageName, origin))) return;
mCache.add(combine(packageName, origin));
;
try {
ApplicationInfo ai = mPackageManager.getApplicationInfo(packageName, 0);
String appLabel = mPackageManager.getApplicationLabel(ai).toString();
if (TextUtils.isEmpty(appLabel) || ai.uid == -1) {
Log.e(TAG, "Invalid details for client package %s: %d, %d",
packageName, ai.uid, appLabel);
return;
}
Log.d(TAG, "Registering %d (%s) for %s", ai.uid, appLabel, origin);
mClientAppDataRegister.registerPackageForOrigin(ai.uid, appLabel, origin);
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Couldn't find name for client package %s", packageName);
}
}
private String combine(String packageName, Origin origin) {
return packageName + ":" + origin.toString();
}
}
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.browserservices;
import android.content.Context;
import android.content.SharedPreferences;
import android.support.annotation.Nullable;
import org.chromium.base.ContextUtils;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* Records whether Chrome has data relevant to a Trusted Web Activity Client.
*
* Lifecycle: Most of the data used by this class modifies the underlying {@link SharedPreferences}
* (which are global and preserved across Chrome restarts).
* Thread safety: This object should only be accessed on a single thread at any time.
*/
public class ClientAppDataRegister {
private static final String PREFS_FILE = "trusted_web_activity_client_apps";
private static final String UIDS_KEY = "trusted_web_activity_uids";
/* Preferences unique to this class. */
private final SharedPreferences mPreferences;
/** Creates a ClientAppDataRegister. */
public ClientAppDataRegister() {
mPreferences = ContextUtils.getApplicationContext()
.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE);
}
/**
* Saves to Preferences that the app with |uid| has the application name |appName| and when it
* is removed or cleared, we should consider doing the same with Chrome data relevant to
* |origin|.
*/
/* package */ void registerPackageForOrigin(int uid, String appName, Origin origin) {
// Store the UID in the main Chrome Preferences.
Set<String> uids = getUids();
uids.add(String.valueOf(uid));
setUids(uids);
// Store the package name for the UID.
mPreferences.edit().putString(createAppNameKey(uid), appName).apply();
// Store the origin for the UID.
String key = createOriginsKey(uid);
Set<String> origins = new HashSet<>(mPreferences.getStringSet(key, Collections.emptySet()));
origins.add(origin.toString());
mPreferences.edit().putStringSet(key, origins).apply();
}
private void setUids(Set<String> uids) {
mPreferences.edit().putStringSet(UIDS_KEY, uids).apply();
}
private Set<String> getUids() {
return new HashSet<>(mPreferences.getStringSet(UIDS_KEY, Collections.emptySet()));
}
/* package */ void removePackage(int uid) {
Set<String> uids = getUids();
uids.remove(String.valueOf(uid));
setUids(uids);
mPreferences.edit().putString(createAppNameKey(uid), null).apply();
mPreferences.edit().putStringSet(createOriginsKey(uid), null).apply();
}
/* package */ boolean chromeHoldsDataForPackage(int uid) {
return getUids().contains(String.valueOf(uid));
}
/**
* Gets the package name that was previously registered for the uid.
*/
/* package */ @Nullable String getAppNameForRegisteredUid(int uid) {
return mPreferences.getString(createAppNameKey(uid), null);
}
/**
* Gets all the origins that have been registered for the uid.
* Do not modify the set returned by this method.
*/
/* package */ Set<String> getOriginsForRegisteredUid(int uid) {
return mPreferences.getStringSet(createOriginsKey(uid), Collections.emptySet());
}
/**
* Creates the Preferences key to access the app name.
* If you modify this you'll have to migrate old data.
*/
private static String createAppNameKey(int uid) {
return uid + ".appName";
}
/**
* Creates the Preferences key to access the set of origins for an app.
* If you modify this you'll have to migrate old data.
*/
private static String createOriginsKey(int uid) {
return uid + ".origins";
}
}
...@@ -30,6 +30,7 @@ public class TrustedWebActivityUi { ...@@ -30,6 +30,7 @@ public class TrustedWebActivityUi {
private final TrustedWebActivityDisclosure mDisclosure; private final TrustedWebActivityDisclosure mDisclosure;
private final TrustedWebActivityOpenTimeRecorder mOpenTimeRecorder = private final TrustedWebActivityOpenTimeRecorder mOpenTimeRecorder =
new TrustedWebActivityOpenTimeRecorder(); new TrustedWebActivityOpenTimeRecorder();
private final ClientAppDataRecorder mClientAppDataRecorder;
private boolean mInTrustedWebActivity = true; private boolean mInTrustedWebActivity = true;
...@@ -88,16 +89,20 @@ public class TrustedWebActivityUi { ...@@ -88,16 +89,20 @@ public class TrustedWebActivityUi {
// This doesn't perform a network request or attempt new verification - it checks to // This doesn't perform a network request or attempt new verification - it checks to
// see if a verification already exists for the given inputs. // see if a verification already exists for the given inputs.
setTrustedWebActivityMode( Origin origin = new Origin(url);
OriginVerifier.isValidOrigin(packageName, new Origin(url), RELATIONSHIP), tab); boolean verified =
OriginVerifier.isValidOrigin(packageName, origin, RELATIONSHIP);
if (verified) registerClientAppData(packageName, origin);
setTrustedWebActivityMode(verified, tab);
} }
}; };
/** Creates a TrustedWebActivityUi, providing a delegate from the embedder. */ /** Creates a TrustedWebActivityUi, providing a delegate from the embedder. */
public TrustedWebActivityUi(TrustedWebActivityUiDelegate delegate, public TrustedWebActivityUi(TrustedWebActivityUiDelegate delegate,
TrustedWebActivityDisclosure disclosure) { TrustedWebActivityDisclosure disclosure, ClientAppDataRecorder clientAppDataRecorder) {
mDelegate = delegate; mDelegate = delegate;
mClientAppDataRecorder = clientAppDataRecorder;
mDisclosure = disclosure; mDisclosure = disclosure;
} }
...@@ -146,6 +151,7 @@ public class TrustedWebActivityUi { ...@@ -146,6 +151,7 @@ public class TrustedWebActivityUi {
if (!origin.equals(new Origin(tab.getUrl()))) return; if (!origin.equals(new Origin(tab.getUrl()))) return;
BrowserServicesMetrics.recordTwaOpened(); BrowserServicesMetrics.recordTwaOpened();
if (verified) registerClientAppData(packageName, origin);
setTrustedWebActivityMode(verified, tab); setTrustedWebActivityMode(verified, tab);
}, packageName, RELATIONSHIP).start(origin); }, packageName, RELATIONSHIP).start(origin);
} }
...@@ -173,6 +179,7 @@ public class TrustedWebActivityUi { ...@@ -173,6 +179,7 @@ public class TrustedWebActivityUi {
if (enabled) { if (enabled) {
mDisclosure.showSnackbarIfNeeded(mDelegate.getSnackbarManager(), mDisclosure.showSnackbarIfNeeded(mDelegate.getSnackbarManager(),
mDelegate.getClientPackageName()); mDelegate.getClientPackageName());
} else { } else {
// Force showing the controls for a bit when leaving Trusted Web Activity mode. // Force showing the controls for a bit when leaving Trusted Web Activity mode.
mDelegate.getBrowserStateBrowserControlsVisibilityDelegate().showControlsTransient(); mDelegate.getBrowserStateBrowserControlsVisibilityDelegate().showControlsTransient();
...@@ -183,4 +190,26 @@ public class TrustedWebActivityUi { ...@@ -183,4 +190,26 @@ public class TrustedWebActivityUi {
tab.updateFullscreenEnabledState(); tab.updateFullscreenEnabledState();
} }
/**
* Register that we have Chrome data relevant to the Client app.
*
* We do this here, when the Trusted Web Activity UI is shown instead of in OriginVerifier when
* verification completes because when an origin is being verified, we don't know whether it is
* for the purposes of Trusted Web Activities or for Post Message (where this behaviour is not
* required).
*
* Additionally we do it on every page navigation because an app can be verified for more than
* one Origin, eg:
* 1) App verifies with https://www.myfirsttwa.com/.
* 2) App verifies with https://www.mysecondtwa.com/.
* 3) App launches a TWA to https://www.myfirsttwa.com/.
* 4) App navigates to https://www.mysecondtwa.com/.
*
* At step 2, we don't know why the app is verifying with that origin (it could be for TWAs or
* for PostMessage). Only at step 4 do we know that Chrome should associate the browsing data
* for that origin with that app.
*/
private void registerClientAppData(String packageName, Origin origin) {
mClientAppDataRecorder.register(packageName, origin);
}
} }
...@@ -65,6 +65,8 @@ import org.chromium.chrome.browser.appmenu.AppMenuPropertiesDelegate; ...@@ -65,6 +65,8 @@ import org.chromium.chrome.browser.appmenu.AppMenuPropertiesDelegate;
import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiController; import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiController;
import org.chromium.chrome.browser.browserservices.BrowserSessionContentHandler; import org.chromium.chrome.browser.browserservices.BrowserSessionContentHandler;
import org.chromium.chrome.browser.browserservices.BrowserSessionContentUtils; import org.chromium.chrome.browser.browserservices.BrowserSessionContentUtils;
import org.chromium.chrome.browser.browserservices.ClientAppDataRecorder;
import org.chromium.chrome.browser.browserservices.ClientAppDataRegister;
import org.chromium.chrome.browser.browserservices.TrustedWebActivityUi; import org.chromium.chrome.browser.browserservices.TrustedWebActivityUi;
import org.chromium.chrome.browser.compositor.layouts.LayoutManager; import org.chromium.chrome.browser.compositor.layouts.LayoutManager;
import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsModule; import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsModule;
...@@ -326,7 +328,8 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -326,7 +328,8 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
public SnackbarManager getSnackbarManager() { public SnackbarManager getSnackbarManager() {
return CustomTabActivity.this.getSnackbarManager(); return CustomTabActivity.this.getSnackbarManager();
} }
}, getComponent().getTrustedWebActivityDisclosure()); }, getComponent().getTrustedWebActivityDisclosure(),
new ClientAppDataRecorder(getPackageManager(), new ClientAppDataRegister()));
} }
/** /**
......
...@@ -19,7 +19,9 @@ public class NotificationBuilderFactory { ...@@ -19,7 +19,9 @@ public class NotificationBuilderFactory {
* Creates either a Notification.Builder or NotificationCompat.Builder under the hood, wrapped * Creates either a Notification.Builder or NotificationCompat.Builder under the hood, wrapped
* in our own common interface, and ensures the notification channel has been initialized. * in our own common interface, and ensures the notification channel has been initialized.
* *
* @param preferCompat true if a NotificationCompat.Builder is preferred. * @param preferCompat true if a NotificationCompat.Builder is preferred. You should pick true
* unless you know NotificationCompat.Builder doesn't support a feature you
* require.
* @param channelId The ID of the channel the notification should be posted to. This channel * @param channelId The ID of the channel the notification should be posted to. This channel
* will be created if it did not already exist. Must be a known channel within * will be created if it did not already exist. Must be a known channel within
* {@link ChannelsInitializer#ensureInitialized(String)}. * {@link ChannelsInitializer#ensureInitialized(String)}.
......
...@@ -160,6 +160,9 @@ chrome_java_sources = [ ...@@ -160,6 +160,9 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/browserservices/BrowserSessionContentHandler.java", "java/src/org/chromium/chrome/browser/browserservices/BrowserSessionContentHandler.java",
"java/src/org/chromium/chrome/browser/browserservices/BrowserSessionContentUtils.java", "java/src/org/chromium/chrome/browser/browserservices/BrowserSessionContentUtils.java",
"java/src/org/chromium/chrome/browser/browserservices/BrowserSessionDataProvider.java", "java/src/org/chromium/chrome/browser/browserservices/BrowserSessionDataProvider.java",
"java/src/org/chromium/chrome/browser/browserservices/ClearDataBroadcastReceiver.java",
"java/src/org/chromium/chrome/browser/browserservices/ClientAppDataRecorder.java",
"java/src/org/chromium/chrome/browser/browserservices/ClientAppDataRegister.java",
"java/src/org/chromium/chrome/browser/browserservices/Origin.java", "java/src/org/chromium/chrome/browser/browserservices/Origin.java",
"java/src/org/chromium/chrome/browser/browserservices/OriginVerifier.java", "java/src/org/chromium/chrome/browser/browserservices/OriginVerifier.java",
"java/src/org/chromium/chrome/browser/browserservices/PostMessageHandler.java", "java/src/org/chromium/chrome/browser/browserservices/PostMessageHandler.java",
...@@ -2197,6 +2200,8 @@ chrome_junit_test_java_sources = [ ...@@ -2197,6 +2200,8 @@ chrome_junit_test_java_sources = [
"junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetControllerTest.java", "junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetControllerTest.java",
"junit/src/org/chromium/chrome/browser/background_task_scheduler/NativeBackgroundTaskTest.java", "junit/src/org/chromium/chrome/browser/background_task_scheduler/NativeBackgroundTaskTest.java",
"junit/src/org/chromium/chrome/browser/browseractions/BrowserActionsIntentTest.java", "junit/src/org/chromium/chrome/browser/browseractions/BrowserActionsIntentTest.java",
"junit/src/org/chromium/chrome/browser/browserservices/ClientAppDataRecorderTest.java",
"junit/src/org/chromium/chrome/browser/browserservices/ClientAppDataRegisterTest.java",
"junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityDisclosureTest.java", "junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityDisclosureTest.java",
"junit/src/org/chromium/chrome/browser/compositor/animation/CompositorAnimatorTest.java", "junit/src/org/chromium/chrome/browser/compositor/animation/CompositorAnimatorTest.java",
"junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java", "junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java",
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.browserservices;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.Feature;
/**
* Tests for {@link ClientAppDataRecorder}.
*/
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class ClientAppDataRecorderTest {
private static final int APP_UID = 123;
private static final String APP_NAME = "Example App";
private static final String APP_PACKAGE = "com.example.app";
private static final String MISSING_PACKAGE = "com.missing.app";
private static final Origin ORIGIN = new Origin("https://www.example.com/");
private static final Origin OTHER_ORIGIN = new Origin("https://www.example.com/");
@Mock public ClientAppDataRegister mRegister;
@Mock public PackageManager mPackageManager;
private ClientAppDataRecorder mRecorder;
@Before
public void setUp() throws PackageManager.NameNotFoundException {
MockitoAnnotations.initMocks(this);
ApplicationInfo appInfo = new ApplicationInfo();
appInfo.uid = APP_UID;
// Even though we're not actually calling getApplicationInfo here, the code needs to deal
// with a checked exception.
doReturn(appInfo).when(mPackageManager).getApplicationInfo(eq(APP_PACKAGE), anyInt());
doReturn(APP_NAME).when(mPackageManager).getApplicationLabel(appInfo);
doThrow(new PackageManager.NameNotFoundException())
.when(mPackageManager)
.getApplicationInfo(eq(MISSING_PACKAGE), anyInt());
mRecorder = new ClientAppDataRecorder(mPackageManager, mRegister);
}
@Test
@Feature("TrustedWebActivities")
public void testRegister() {
mRecorder.register(APP_PACKAGE, ORIGIN);
verify(mRegister).registerPackageForOrigin(APP_UID, APP_NAME, ORIGIN);
}
@Test
@Feature("TrustedWebActivities")
public void testDeduplicate() {
mRecorder.register(APP_PACKAGE, ORIGIN);
mRecorder.register(APP_PACKAGE, ORIGIN);
verify(mRegister).registerPackageForOrigin(APP_UID, APP_NAME, ORIGIN);
}
@Test
@Feature("TrustedWebActivities")
public void testDifferentOrigins() {
mRecorder.register(APP_PACKAGE, ORIGIN);
mRecorder.register(APP_PACKAGE, OTHER_ORIGIN);
verify(mRegister).registerPackageForOrigin(APP_UID, APP_NAME, ORIGIN);
verify(mRegister).registerPackageForOrigin(APP_UID, APP_NAME, OTHER_ORIGIN);
}
@Test
@Feature("TrustedWebActivities")
public void testMisingPackage() {
mRecorder.register(MISSING_PACKAGE, ORIGIN);
// Implicitly checking we don't throw.
verify(mRegister, times(0)).registerPackageForOrigin(anyInt(), anyString(), any());
}
}
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.browserservices;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.Feature;
import java.util.Set;
/**
* Tests for {@link ClientAppDataRegister}.
*/
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class ClientAppDataRegisterTest {
private static final int UID = 23;
private static final String APP_NAME = "Example App";
private static final Origin ORIGIN = new Origin("https://www.example.com/");
private static final Origin OTHER_ORIGIN = new Origin("https://other.example.com/");
private ClientAppDataRegister mRegister;
@Before
public void setUp() {
mRegister = new ClientAppDataRegister();
}
@Test
@Feature("TrustedWebActivities")
public void testRegistration() {
mRegister.registerPackageForOrigin(UID, APP_NAME, ORIGIN);
Assert.assertTrue(mRegister.chromeHoldsDataForPackage(UID));
Assert.assertEquals(APP_NAME, mRegister.getAppNameForRegisteredUid(UID));
}
@Test
@Feature("TrustedWebActivities")
public void testDeregistration() {
mRegister.registerPackageForOrigin(UID, APP_NAME, ORIGIN);
mRegister.removePackage(UID);
Assert.assertFalse(mRegister.chromeHoldsDataForPackage(UID));
Assert.assertNull(mRegister.getAppNameForRegisteredUid(UID));
}
@Test
@Feature("TrustedWebActivities")
public void testGetOrigins() {
mRegister.registerPackageForOrigin(UID, APP_NAME, ORIGIN);
mRegister.registerPackageForOrigin(UID, APP_NAME, OTHER_ORIGIN);
Set<String> origins = mRegister.getOriginsForRegisteredUid(UID);
Assert.assertEquals(2, origins.size());
Assert.assertTrue(origins.contains(ORIGIN.toString()));
Assert.assertTrue(origins.contains(OTHER_ORIGIN.toString()));
}
@Test
@Feature("TrustedWebActivities")
public void testClearOrigins() {
mRegister.registerPackageForOrigin(UID, APP_NAME, ORIGIN);
mRegister.registerPackageForOrigin(UID, APP_NAME, OTHER_ORIGIN);
mRegister.removePackage(UID);
Set<String> origins = mRegister.getOriginsForRegisteredUid(UID);
Assert.assertTrue(origins.isEmpty());
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment