Commit c129b43c authored by Robbie McElrath's avatar Robbie McElrath Committed by Commit Bot

[WebLayer] Delete WebLayer data if it is downgraded.

This CL makes WebLayer delete ALL of its data during startup if it
detects that it was downgraded to a lower major release since the
previous launch. The previous version is maintained in a
SharedPreference.

Bug: 1043217
Change-Id: I12c90d6705be4563a2e3bf07712f96a1136d96c9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2052601
Commit-Queue: Robbie McElrath <rmcelrath@chromium.org>
Reviewed-by: default avatarTobias Sargeant <tobiasjs@chromium.org>
Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Cr-Commit-Position: refs/heads/master@{#749385}
parent 055fd141
...@@ -13,6 +13,7 @@ android_library("weblayer_java_tests") { ...@@ -13,6 +13,7 @@ android_library("weblayer_java_tests") {
"src/org/chromium/weblayer/test/CloseTabNewTabCallbackImpl.java", "src/org/chromium/weblayer/test/CloseTabNewTabCallbackImpl.java",
"src/org/chromium/weblayer/test/CrashReporterTest.java", "src/org/chromium/weblayer/test/CrashReporterTest.java",
"src/org/chromium/weblayer/test/DataClearingTest.java", "src/org/chromium/weblayer/test/DataClearingTest.java",
"src/org/chromium/weblayer/test/DowngradeTest.java",
"src/org/chromium/weblayer/test/DownloadCallbackTest.java", "src/org/chromium/weblayer/test/DownloadCallbackTest.java",
"src/org/chromium/weblayer/test/ErrorPageCallbackTest.java", "src/org/chromium/weblayer/test/ErrorPageCallbackTest.java",
"src/org/chromium/weblayer/test/EventUtils.java", "src/org/chromium/weblayer/test/EventUtils.java",
......
// Copyright 2020 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.weblayer.test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.support.test.filters.SmallTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.ContextUtils;
import org.chromium.base.PathUtils;
import org.chromium.weblayer.shell.InstrumentationActivity;
import java.io.File;
import java.io.IOException;
/**
* Tests that WebLayer version changes handle data correctly.
*/
@RunWith(WebLayerJUnit4ClassRunner.class)
public class DowngradeTest {
public static final String PREF_LAST_VERSION_CODE =
"org.chromium.weblayer.last_version_code_used";
@Rule
public InstrumentationActivityTestRule mActivityTestRule =
new InstrumentationActivityTestRule();
// A test file in the app's data directory. This should never get deleted.
private File mAppFile;
// A test file in WebLayer's data directory. This should get deleted when we downgrade.
private File mWebLayerDataFile;
@Before
public void setUp() throws IOException, PackageManager.NameNotFoundException {
PathUtils.setPrivateDataDirectorySuffix("weblayer", "weblayer");
mWebLayerDataFile = new File(PathUtils.getDataDirectory(), "testWebLayerFile");
assertTrue(mWebLayerDataFile.createNewFile());
Context context = ContextUtils.getApplicationContext();
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
mAppFile = new File(packageInfo.applicationInfo.dataDir, "testAppFile");
assertTrue(mAppFile.createNewFile());
}
@After
public void tearDown() {
mAppFile.delete();
mWebLayerDataFile.delete();
}
@Test
@SmallTest
public void testDowngradeDeletesData() throws IOException {
SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
prefs.edit().putInt(PREF_LAST_VERSION_CODE, 9999_000_00).apply();
InstrumentationActivity activity = mActivityTestRule.launchWithProfile("profile");
runOnUiThreadBlocking(
() -> { activity.loadWebLayerSync(ContextUtils.getApplicationContext()); });
assertFalse(mWebLayerDataFile.exists());
assertTrue(mAppFile.exists());
}
@Test
@SmallTest
public void testUnknownLastVersionKeepsData() throws IOException {
SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
assertFalse(prefs.contains(PREF_LAST_VERSION_CODE));
InstrumentationActivity activity = mActivityTestRule.launchWithProfile("profile");
runOnUiThreadBlocking(
() -> { activity.loadWebLayerSync(ContextUtils.getApplicationContext()); });
assertTrue(mWebLayerDataFile.exists());
assertTrue(mAppFile.exists());
}
@Test
@SmallTest
public void testNewVersionKeepsData() {
SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
prefs.edit().putInt(PREF_LAST_VERSION_CODE, 1_000_00).apply();
InstrumentationActivity activity = mActivityTestRule.launchWithProfile("profile");
runOnUiThreadBlocking(
() -> { activity.loadWebLayerSync(ContextUtils.getApplicationContext()); });
assertTrue(mWebLayerDataFile.exists());
assertTrue(mAppFile.exists());
}
}
...@@ -6,6 +6,7 @@ package org.chromium.weblayer_private; ...@@ -6,6 +6,7 @@ package org.chromium.weblayer_private;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.AssetManager; import android.content.res.AssetManager;
...@@ -26,6 +27,7 @@ import org.chromium.base.BundleUtils; ...@@ -26,6 +27,7 @@ import org.chromium.base.BundleUtils;
import org.chromium.base.CommandLine; import org.chromium.base.CommandLine;
import org.chromium.base.ContentUriUtils; import org.chromium.base.ContentUriUtils;
import org.chromium.base.ContextUtils; import org.chromium.base.ContextUtils;
import org.chromium.base.FileUtils;
import org.chromium.base.Log; import org.chromium.base.Log;
import org.chromium.base.PathUtils; import org.chromium.base.PathUtils;
import org.chromium.base.StrictModeContext; import org.chromium.base.StrictModeContext;
...@@ -78,6 +80,9 @@ public final class WebLayerImpl extends IWebLayer.Stub { ...@@ -78,6 +80,9 @@ public final class WebLayerImpl extends IWebLayer.Stub {
// signature requirements on the implementation, nor does it use the production code path to // signature requirements on the implementation, nor does it use the production code path to
// load the code. Do not set this in production APKs! // load the code. Do not set this in production APKs!
private static final String PACKAGE_MANIFEST_KEY = "org.chromium.weblayer.WebLayerPackage"; private static final String PACKAGE_MANIFEST_KEY = "org.chromium.weblayer.WebLayerPackage";
// SharedPreferences key storing the versionCode of the most recently loaded WebLayer library.
public static final String PREF_LAST_VERSION_CODE =
"org.chromium.weblayer.last_version_code_used";
private final ProfileManager mProfileManager = new ProfileManager(); private final ProfileManager mProfileManager = new ProfileManager();
...@@ -233,11 +238,10 @@ public final class WebLayerImpl extends IWebLayer.Stub { ...@@ -233,11 +238,10 @@ public final class WebLayerImpl extends IWebLayer.Stub {
} }
} }
// Creating the Android shared preferences object causes I/O. Prewarm during // Creating the Android shared preferences object causes I/O.
// initialization to avoid this occurring randomly later.
// TODO: Do this on a background thread.
try (StrictModeContext ignored = StrictModeContext.allowDiskWrites()) { try (StrictModeContext ignored = StrictModeContext.allowDiskWrites()) {
ContextUtils.getAppSharedPreferences(); SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
deleteDataIfPackageDowngrade(prefs, packageInfo);
} }
DeviceUtils.addDeviceSpecificUserAgentSwitch(); DeviceUtils.addDeviceSpecificUserAgentSwitch();
...@@ -457,6 +461,49 @@ public final class WebLayerImpl extends IWebLayer.Stub { ...@@ -457,6 +461,49 @@ public final class WebLayerImpl extends IWebLayer.Stub {
} }
} }
private static void deleteDataIfPackageDowngrade(
SharedPreferences prefs, PackageInfo packageInfo) {
int previousVersion = prefs.getInt(PREF_LAST_VERSION_CODE, 0);
int currentVersion = packageInfo.versionCode;
if (getBranchFromVersionCode(currentVersion) < getBranchFromVersionCode(previousVersion)) {
// WebLayer was downgraded since the last run. Delete the data and cache directories.
File dataDir = new File(PathUtils.getDataDirectory());
Log.i(TAG,
"WebLayer package downgraded from " + previousVersion + " to " + currentVersion
+ "; deleting contents of " + dataDir);
deleteDirectoryContents(dataDir);
}
if (previousVersion != currentVersion) {
prefs.edit().putInt(PREF_LAST_VERSION_CODE, currentVersion).apply();
}
}
/**
* Chromium versionCodes follow the scheme "BBBBPPPAX":
* BBBB: 4 digit branch number. It monotonically increases over time.
* PPP: Patch number in the branch. It is padded with zeroes to the left. These three digits
* may change their meaning in the future.
* A: Architecture digit.
* X: A digit to differentiate APKs for other reasons.
*
* @return The branch number of versionCode.
*/
private static int getBranchFromVersionCode(int versionCode) {
return versionCode / 1_000_00;
}
private static void deleteDirectoryContents(File directory) {
File[] files = directory.listFiles();
if (files == null) {
return;
}
for (File file : files) {
if (!FileUtils.recursivelyDeleteFile(file, FileUtils.DELETE_ALL)) {
Log.w(TAG, "Failed to delete " + file);
}
}
}
@NativeMethods @NativeMethods
interface Natives { interface Natives {
void setRemoteDebuggingEnabled(boolean enabled); void setRemoteDebuggingEnabled(boolean enabled);
......
...@@ -27,6 +27,7 @@ android_library("weblayer_shell_java") { ...@@ -27,6 +27,7 @@ android_library("weblayer_shell_java") {
deps = [ deps = [
":weblayer_shell_resources", ":weblayer_shell_resources",
"//base:base_java",
"//third_party/android_deps:android_support_v4_java", "//third_party/android_deps:android_support_v4_java",
"//weblayer/public/java", "//weblayer/public/java",
] ]
......
noparent = True noparent = True
include_rules = [ include_rules = [
"+base/android/java",
"+weblayer/public/java", "+weblayer/public/java",
] ]
...@@ -21,6 +21,7 @@ import android.widget.EditText; ...@@ -21,6 +21,7 @@ import android.widget.EditText;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import org.chromium.base.ContextUtils;
import org.chromium.weblayer.Browser; import org.chromium.weblayer.Browser;
import org.chromium.weblayer.Profile; import org.chromium.weblayer.Profile;
import org.chromium.weblayer.Tab; import org.chromium.weblayer.Tab;
...@@ -149,7 +150,8 @@ public class InstrumentationActivity extends FragmentActivity { ...@@ -149,7 +150,8 @@ public class InstrumentationActivity extends FragmentActivity {
private void createWebLayerAsync() { private void createWebLayerAsync() {
try { try {
WebLayer.loadAsync(getApplicationContext(), webLayer -> onWebLayerReady()); // Get the Context from ContextUtils so tests get the wrapped version.
WebLayer.loadAsync(ContextUtils.getApplicationContext(), webLayer -> onWebLayerReady());
} catch (UnsupportedVersionException e) { } catch (UnsupportedVersionException e) {
throw new RuntimeException("Failed to initialize WebLayer", e); throw new RuntimeException("Failed to initialize WebLayer", e);
} }
......
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