Merge "Add tests about MultiDex corruption recovering"
This commit is contained in:
@@ -36,10 +36,8 @@ LOCAL_DEX_PREOPT := false
|
|||||||
|
|
||||||
include $(BUILD_PACKAGE)
|
include $(BUILD_PACKAGE)
|
||||||
|
|
||||||
ifndef LOCAL_JACK_ENABLED
|
|
||||||
$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
|
$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
|
||||||
$(hide) mkdir -p $(dir $@)
|
$(hide) mkdir -p $(dir $@)
|
||||||
$(MAINDEXCLASSES) $< 1>$@
|
$(MAINDEXCLASSES) $< 1>$@
|
||||||
|
|
||||||
$(built_dex_intermediate): $(mainDexList)
|
$(built_dex_intermediate): $(mainDexList)
|
||||||
endif
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
<uses-sdk
|
<uses-sdk
|
||||||
android:minSdkVersion="9"
|
android:minSdkVersion="9"
|
||||||
android:targetSdkVersion="19" />
|
android:targetSdkVersion="19" />
|
||||||
|
<!-- Required for com.android.framework.multidexlegacytestservices.test2 -->
|
||||||
|
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="MultiDexLegacyTestServices">
|
android:label="MultiDexLegacyTestServices">
|
||||||
@@ -124,6 +126,6 @@
|
|||||||
<action android:name="com.android.framework.multidexlegacytestservices.action.Service19" />
|
<action android:name="com.android.framework.multidexlegacytestservices.action.Service19" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -60,35 +60,40 @@ public abstract class AbstractService extends Service implements Runnable {
|
|||||||
// of the result file will be too big.
|
// of the result file will be too big.
|
||||||
RandomAccessFile raf = new RandomAccessFile(resultFile, "rw");
|
RandomAccessFile raf = new RandomAccessFile(resultFile, "rw");
|
||||||
raf.seek(raf.length());
|
raf.seek(raf.length());
|
||||||
Log.i(TAG, "Writing 0x42434445 at " + raf.length() + " in " + resultFile.getPath());
|
if (raf.length() == 0) {
|
||||||
raf.writeInt(0x42434445);
|
Log.i(TAG, "Writing 0x42434445 at " + raf.length() + " in " + resultFile.getPath());
|
||||||
|
raf.writeInt(0x42434445);
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Service was restarted appending 0x42434445 twice at " + raf.length()
|
||||||
|
+ " in " + resultFile.getPath());
|
||||||
|
raf.writeInt(0x42434445);
|
||||||
|
raf.writeInt(0x42434445);
|
||||||
|
}
|
||||||
raf.close();
|
raf.close();
|
||||||
} catch (IOException e) {
|
MultiDex.install(applicationContext);
|
||||||
e.printStackTrace();
|
Log.i(TAG, "Multi dex installation done.");
|
||||||
}
|
|
||||||
MultiDex.install(applicationContext);
|
|
||||||
Log.i(TAG, "Multi dex installation done.");
|
|
||||||
|
|
||||||
int value = getValue();
|
int value = getValue();
|
||||||
Log.i(TAG, "Saving the result (" + value + ") to " + resultFile.getPath());
|
Log.i(TAG, "Saving the result (" + value + ") to " + resultFile.getPath());
|
||||||
try {
|
|
||||||
// Append the check value in result file, keeping the constant values already written.
|
// Append the check value in result file, keeping the constant values already written.
|
||||||
RandomAccessFile raf = new RandomAccessFile(resultFile, "rw");
|
raf = new RandomAccessFile(resultFile, "rw");
|
||||||
raf.seek(raf.length());
|
raf.seek(raf.length());
|
||||||
Log.i(TAG, "Writing result at " + raf.length() + " in " + resultFile.getPath());
|
Log.i(TAG, "Writing result at " + raf.length() + " in " + resultFile.getPath());
|
||||||
raf.writeInt(value);
|
raf.writeInt(value);
|
||||||
raf.close();
|
raf.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
throw new AssertionError(e);
|
||||||
}
|
} finally {
|
||||||
try {
|
try {
|
||||||
// Writing end of processing flags, the existence of the file is the criteria
|
// Writing end of processing flags, the existence of the file is the criteria
|
||||||
RandomAccessFile raf = new RandomAccessFile(new File(applicationContext.getFilesDir(), getId() + ".complete"), "rw");
|
RandomAccessFile raf = new RandomAccessFile(
|
||||||
Log.i(TAG, "creating complete file " + resultFile.getPath());
|
new File(applicationContext.getFilesDir(), getId() + ".complete"), "rw");
|
||||||
raf.writeInt(0x32333435);
|
Log.i(TAG, "creating complete file " + resultFile.getPath());
|
||||||
raf.close();
|
raf.writeInt(0x32333435);
|
||||||
} catch (IOException e) {
|
raf.close();
|
||||||
e.printStackTrace();
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,9 +124,10 @@ public abstract class AbstractService extends Service implements Runnable {
|
|||||||
intermediate = ReflectIntermediateClass.get(45, 80, 20 /* 5 seems enough on a nakasi,
|
intermediate = ReflectIntermediateClass.get(45, 80, 20 /* 5 seems enough on a nakasi,
|
||||||
using 20 to get some margin */);
|
using 20 to get some margin */);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
int value = new com.android.framework.multidexlegacytestservices.manymethods.Big001().get1() +
|
int value =
|
||||||
|
new com.android.framework.multidexlegacytestservices.manymethods.Big001().get1() +
|
||||||
new com.android.framework.multidexlegacytestservices.manymethods.Big002().get2() +
|
new com.android.framework.multidexlegacytestservices.manymethods.Big002().get2() +
|
||||||
new com.android.framework.multidexlegacytestservices.manymethods.Big003().get3() +
|
new com.android.framework.multidexlegacytestservices.manymethods.Big003().get3() +
|
||||||
new com.android.framework.multidexlegacytestservices.manymethods.Big004().get4() +
|
new com.android.framework.multidexlegacytestservices.manymethods.Big004().get4() +
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# Copyright (C) 2014 The Android Open Source Project
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
LOCAL_PATH:= $(call my-dir)
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_MODULE_TAGS := tests
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||||
|
|
||||||
|
LOCAL_PACKAGE_NAME := MultiDexLegacyTestServicesTests2
|
||||||
|
|
||||||
|
LOCAL_JAVA_LIBRARIES := android-support-multidex
|
||||||
|
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
|
||||||
|
|
||||||
|
LOCAL_SDK_VERSION := 9
|
||||||
|
|
||||||
|
LOCAL_DEX_PREOPT := false
|
||||||
|
|
||||||
|
include $(BUILD_PACKAGE)
|
||||||
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.android.framework.multidexlegacytestservices.test2"
|
||||||
|
android:versionCode="1"
|
||||||
|
android:versionName="1.0" >
|
||||||
|
|
||||||
|
<uses-sdk android:minSdkVersion="9" />
|
||||||
|
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
|
||||||
|
<instrumentation
|
||||||
|
android:name="android.support.test.runner.AndroidJUnitRunner"
|
||||||
|
android:targetPackage="com.android.framework.multidexlegacytestservices" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:label="multidexlegacytestservices.test2" >
|
||||||
|
<uses-library android:name="android.test.runner" />
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,381 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.framework.multidexlegacytestservices.test2;
|
||||||
|
|
||||||
|
import android.app.ActivityManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.support.test.InstrumentationRegistry;
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.util.Log;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileFilter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import junit.framework.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the tests with: <code>adb shell am instrument -w
|
||||||
|
* com.android.framework.multidexlegacytestservices.test2/android.support.test.runner.AndroidJUnitRunner
|
||||||
|
* </code>
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class ServicesTests {
|
||||||
|
private static final String TAG = "ServicesTests";
|
||||||
|
|
||||||
|
static {
|
||||||
|
Log.i(TAG, "Initializing");
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ExtensionFilter implements FileFilter {
|
||||||
|
private final String ext;
|
||||||
|
|
||||||
|
public ExtensionFilter(String ext) {
|
||||||
|
this.ext = ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean accept(File file) {
|
||||||
|
return file.getName().endsWith(ext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ExtractedZipFilter extends ExtensionFilter {
|
||||||
|
public ExtractedZipFilter() {
|
||||||
|
super(".zip");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean accept(File file) {
|
||||||
|
return super.accept(file) && !file.getName().startsWith("tmp-");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int ENDHDR = 22;
|
||||||
|
|
||||||
|
private static final String SERVICE_BASE_ACTION =
|
||||||
|
"com.android.framework.multidexlegacytestservices.action.Service";
|
||||||
|
private static final int MIN_SERVICE = 1;
|
||||||
|
private static final int MAX_SERVICE = 19;
|
||||||
|
private static final String COMPLETION_SUCCESS = "Success";
|
||||||
|
|
||||||
|
private File targetFilesDir;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() throws Exception {
|
||||||
|
Log.i(TAG, "setup");
|
||||||
|
killServices();
|
||||||
|
|
||||||
|
File applicationDataDir =
|
||||||
|
new File(InstrumentationRegistry.getTargetContext().getApplicationInfo().dataDir);
|
||||||
|
clearDirContent(applicationDataDir);
|
||||||
|
targetFilesDir = InstrumentationRegistry.getTargetContext().getFilesDir();
|
||||||
|
|
||||||
|
Log.i(TAG, "setup done");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStressConcurentLaunch() throws Exception {
|
||||||
|
startServices();
|
||||||
|
waitServicesCompletion();
|
||||||
|
String completionStatus = getServicesCompletionStatus();
|
||||||
|
if (completionStatus != COMPLETION_SUCCESS) {
|
||||||
|
Assert.fail(completionStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRecoverFromZipCorruption() throws Exception {
|
||||||
|
int serviceId = 1;
|
||||||
|
// Ensure extraction.
|
||||||
|
initServicesWorkFiles();
|
||||||
|
startService(serviceId);
|
||||||
|
waitServicesCompletion(serviceId);
|
||||||
|
|
||||||
|
// Corruption of the extracted zips.
|
||||||
|
tamperAllExtractedZips();
|
||||||
|
|
||||||
|
killServices();
|
||||||
|
checkRecover();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRecoverFromDexCorruption() throws Exception {
|
||||||
|
int serviceId = 1;
|
||||||
|
// Ensure extraction.
|
||||||
|
initServicesWorkFiles();
|
||||||
|
startService(serviceId);
|
||||||
|
waitServicesCompletion(serviceId);
|
||||||
|
|
||||||
|
// Corruption of the odex files.
|
||||||
|
tamperAllOdex();
|
||||||
|
|
||||||
|
killServices();
|
||||||
|
checkRecover();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRecoverFromZipCorruptionStressTest() throws Exception {
|
||||||
|
Thread startServices =
|
||||||
|
new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
startServices();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
startServices.start();
|
||||||
|
|
||||||
|
// Start services lasts more than 80s, lets cause a few corruptions during this interval.
|
||||||
|
for (int i = 0; i < 80; i++) {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
tamperAllExtractedZips();
|
||||||
|
}
|
||||||
|
startServices.join();
|
||||||
|
try {
|
||||||
|
waitServicesCompletion();
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
// Can happen.
|
||||||
|
}
|
||||||
|
|
||||||
|
killServices();
|
||||||
|
checkRecover();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRecoverFromDexCorruptionStressTest() throws Exception {
|
||||||
|
Thread startServices =
|
||||||
|
new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
startServices();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
startServices.start();
|
||||||
|
|
||||||
|
// Start services lasts more than 80s, lets cause a few corruptions during this interval.
|
||||||
|
for (int i = 0; i < 80; i++) {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
tamperAllOdex();
|
||||||
|
}
|
||||||
|
startServices.join();
|
||||||
|
try {
|
||||||
|
waitServicesCompletion();
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
// Will probably happen most of the time considering what we're doing...
|
||||||
|
}
|
||||||
|
|
||||||
|
killServices();
|
||||||
|
checkRecover();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void clearDirContent(File dir) {
|
||||||
|
for (File subElement : dir.listFiles()) {
|
||||||
|
if (subElement.isDirectory()) {
|
||||||
|
clearDirContent(subElement);
|
||||||
|
}
|
||||||
|
if (!subElement.delete()) {
|
||||||
|
throw new AssertionError("Failed to clear '" + subElement.getAbsolutePath() + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startServices() {
|
||||||
|
Log.i(TAG, "start services");
|
||||||
|
initServicesWorkFiles();
|
||||||
|
for (int i = MIN_SERVICE; i <= MAX_SERVICE; i++) {
|
||||||
|
startService(i);
|
||||||
|
try {
|
||||||
|
Thread.sleep((i - 1) * (1 << (i / 5)));
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startService(int serviceId) {
|
||||||
|
Log.i(TAG, "start service " + serviceId);
|
||||||
|
InstrumentationRegistry.getContext().startService(new Intent(SERVICE_BASE_ACTION + serviceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initServicesWorkFiles() {
|
||||||
|
for (int i = MIN_SERVICE; i <= MAX_SERVICE; i++) {
|
||||||
|
File resultFile = new File(targetFilesDir, "Service" + i);
|
||||||
|
resultFile.delete();
|
||||||
|
Assert.assertFalse(
|
||||||
|
"Failed to delete result file '" + resultFile.getAbsolutePath() + "'.",
|
||||||
|
resultFile.exists());
|
||||||
|
File completeFile = new File(targetFilesDir, "Service" + i + ".complete");
|
||||||
|
completeFile.delete();
|
||||||
|
Assert.assertFalse(
|
||||||
|
"Failed to delete completion file '" + completeFile.getAbsolutePath() + "'.",
|
||||||
|
completeFile.exists());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void waitServicesCompletion() throws TimeoutException {
|
||||||
|
Log.i(TAG, "start sleeping");
|
||||||
|
int attempt = 0;
|
||||||
|
int maxAttempt = 50; // 10 is enough for a nexus S
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
Thread.sleep(5000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
attempt++;
|
||||||
|
if (attempt >= maxAttempt) {
|
||||||
|
throw new TimeoutException();
|
||||||
|
}
|
||||||
|
} while (!areAllServicesCompleted());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void waitServicesCompletion(int serviceId) throws TimeoutException {
|
||||||
|
Log.i(TAG, "start sleeping");
|
||||||
|
int attempt = 0;
|
||||||
|
int maxAttempt = 50; // 10 is enough for a nexus S
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
Thread.sleep(5000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
attempt++;
|
||||||
|
if (attempt >= maxAttempt) {
|
||||||
|
throw new TimeoutException();
|
||||||
|
}
|
||||||
|
} while (isServiceRunning(serviceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getServicesCompletionStatus() {
|
||||||
|
String status = COMPLETION_SUCCESS;
|
||||||
|
for (int i = MIN_SERVICE; i <= MAX_SERVICE; i++) {
|
||||||
|
File resultFile = new File(targetFilesDir, "Service" + i);
|
||||||
|
if (!resultFile.isFile()) {
|
||||||
|
status = "Service" + i + " never completed.";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (resultFile.length() != 8) {
|
||||||
|
status = "Service" + i + " was restarted.";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.i(TAG, "Services completion status: " + status);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getServiceCompletionStatus(int serviceId) {
|
||||||
|
String status = COMPLETION_SUCCESS;
|
||||||
|
File resultFile = new File(targetFilesDir, "Service" + serviceId);
|
||||||
|
if (!resultFile.isFile()) {
|
||||||
|
status = "Service" + serviceId + " never completed.";
|
||||||
|
} else if (resultFile.length() != 8) {
|
||||||
|
status = "Service" + serviceId + " was restarted.";
|
||||||
|
}
|
||||||
|
Log.i(TAG, "Service " + serviceId + " completion status: " + status);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean areAllServicesCompleted() {
|
||||||
|
for (int i = MIN_SERVICE; i <= MAX_SERVICE; i++) {
|
||||||
|
if (isServiceRunning(i)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isServiceRunning(int i) {
|
||||||
|
File completeFile = new File(targetFilesDir, "Service" + i + ".complete");
|
||||||
|
return !completeFile.exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getSecondaryFolder() {
|
||||||
|
File dir =
|
||||||
|
new File(
|
||||||
|
new File(
|
||||||
|
InstrumentationRegistry.getTargetContext().getApplicationInfo().dataDir,
|
||||||
|
"code_cache"),
|
||||||
|
"secondary-dexes");
|
||||||
|
Assert.assertTrue(dir.getAbsolutePath(), dir.isDirectory());
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tamperAllExtractedZips() throws IOException {
|
||||||
|
// First attempt was to just overwrite zip entries but keep central directory, this was no
|
||||||
|
// trouble for Dalvik that was just ignoring those zip and using the odex files.
|
||||||
|
Log.i(TAG, "Tamper extracted zip files by overwriting all content by '\\0's.");
|
||||||
|
byte[] zeros = new byte[4 * 1024];
|
||||||
|
// Do not tamper tmp zip during their extraction.
|
||||||
|
for (File zip : getSecondaryFolder().listFiles(new ExtractedZipFilter())) {
|
||||||
|
long fileLength = zip.length();
|
||||||
|
Assert.assertTrue(fileLength > ENDHDR);
|
||||||
|
zip.setWritable(true);
|
||||||
|
RandomAccessFile raf = new RandomAccessFile(zip, "rw");
|
||||||
|
try {
|
||||||
|
int index = 0;
|
||||||
|
while (index < fileLength) {
|
||||||
|
int length = (int) Math.min(zeros.length, fileLength - index);
|
||||||
|
raf.write(zeros, 0, length);
|
||||||
|
index += length;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
raf.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tamperAllOdex() throws IOException {
|
||||||
|
Log.i(TAG, "Tamper odex files by overwriting some content by '\\0's.");
|
||||||
|
byte[] zeros = new byte[4 * 1024];
|
||||||
|
// I think max size would be 40 (u1[8] + 8 u4) but it's a test so lets take big margins.
|
||||||
|
int savedSizeForOdexHeader = 80;
|
||||||
|
for (File odex : getSecondaryFolder().listFiles(new ExtensionFilter(".dex"))) {
|
||||||
|
long fileLength = odex.length();
|
||||||
|
Assert.assertTrue(fileLength > zeros.length + savedSizeForOdexHeader);
|
||||||
|
odex.setWritable(true);
|
||||||
|
RandomAccessFile raf = new RandomAccessFile(odex, "rw");
|
||||||
|
try {
|
||||||
|
raf.seek(savedSizeForOdexHeader);
|
||||||
|
raf.write(zeros, 0, zeros.length);
|
||||||
|
} finally {
|
||||||
|
raf.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkRecover() throws TimeoutException {
|
||||||
|
Log.i(TAG, "Check recover capability");
|
||||||
|
int serviceId = 1;
|
||||||
|
// Start one service and check it was able to run correctly even if a previous run failed.
|
||||||
|
initServicesWorkFiles();
|
||||||
|
startService(serviceId);
|
||||||
|
waitServicesCompletion(serviceId);
|
||||||
|
String completionStatus = getServiceCompletionStatus(serviceId);
|
||||||
|
if (completionStatus != COMPLETION_SUCCESS) {
|
||||||
|
Assert.fail(completionStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void killServices() {
|
||||||
|
((ActivityManager)
|
||||||
|
InstrumentationRegistry.getContext().getSystemService(Context.ACTIVITY_SERVICE))
|
||||||
|
.killBackgroundProcesses("com.android.framework.multidexlegacytestservices");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user