diff options
-rw-r--r-- | services/core/java/com/android/server/pm/dex/DexLogger.java | 19 | ||||
-rw-r--r-- | services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java | 166 |
2 files changed, 179 insertions, 6 deletions
diff --git a/services/core/java/com/android/server/pm/dex/DexLogger.java b/services/core/java/com/android/server/pm/dex/DexLogger.java index c7bbf1cbc5c6..88d9e52ccf51 100644 --- a/services/core/java/com/android/server/pm/dex/DexLogger.java +++ b/services/core/java/com/android/server/pm/dex/DexLogger.java @@ -27,6 +27,7 @@ import android.util.PackageUtils; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.pm.Installer; import com.android.server.pm.Installer.InstallerException; @@ -57,10 +58,11 @@ public class DexLogger implements DexManager.Listener { return new DexLogger(pms, installer, installLock); } - private DexLogger(IPackageManager pms, Installer installer, Object installLock) { - mPackageManager = pms; - mInstaller = installer; - mInstallLock = installLock; + @VisibleForTesting + /*package*/ DexLogger(IPackageManager pms, Installer installer, Object installLock) { + mPackageManager = pms; + mInstaller = installer; + mInstallLock = installLock; } /** @@ -92,7 +94,7 @@ public class DexLogger implements DexManager.Listener { message = message + ' ' + ByteStringUtils.toHexString(hash); } - EventLog.writeEvent(SNET_TAG, DCL_SUBTAG, ownerUid, message); + writeDclEvent(ownerUid, message); if (dexUseInfo.isUsedByOtherApps()) { Set<String> otherPackages = dexUseInfo.getLoadingPackages(); @@ -109,8 +111,13 @@ public class DexLogger implements DexManager.Listener { } } for (int otherUid : otherUids) { - EventLog.writeEvent(SNET_TAG, DCL_SUBTAG, otherUid, message); + writeDclEvent(otherUid, message); } } } + + @VisibleForTesting + /*package*/ void writeDclEvent(int uid, String message) { + EventLog.writeEvent(SNET_TAG, DCL_SUBTAG, uid, message); + } } diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java new file mode 100644 index 000000000000..bf8d4056ee55 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java @@ -0,0 +1,166 @@ +/* + * Copyright 2017 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.server.pm.dex; + +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; +import android.content.pm.PackageInfo; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.os.storage.StorageManager; + +import com.android.server.pm.Installer; +import com.android.server.pm.Installer.InstallerException; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ListMultimap; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.mockito.quality.Strictness; + +import java.util.Arrays; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; + +import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo; +import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class DexLoggerTests { + private static final String PACKAGE_NAME = "package.name"; + private static final String VOLUME_UUID = "volUuid"; + private static final String DEX_PATH = "/bar/foo.jar"; + private static final int STORAGE_FLAGS = StorageManager.FLAG_STORAGE_DE; + private static final int OWNER_UID = 43; + private static final int OWNER_USER_ID = 44; + + // Obtained via: echo -n "foo.jar" | sha256sum + private static final String DEX_FILENAME_HASH = + "91D7B844D7CC9673748FF057D8DC83972280FC28537D381AA42015A9CF214B9F"; + + private static final byte[] CONTENT_HASH_BYTES = new byte[] { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 + }; + private static final String CONTENT_HASH = + "0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20"; + + @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS); + + @Mock IPackageManager mPM; + @Mock Installer mInstaller; + private final Object mInstallLock = new Object(); + + private DexManager.Listener mListener; + + private final ListMultimap<Integer, String> mMessagesForUid = ArrayListMultimap.create(); + + @Before + public void setup() { + // For test purposes capture log messages as well as sending to the event log. + mListener = new DexLogger(mPM, mInstaller, mInstallLock) { + @Override + void writeDclEvent(int uid, String message) { + super.writeDclEvent(uid, message); + mMessagesForUid.put(uid, message); + } + }; + } + + @Test + public void testSingleAppWithFileHash() throws Exception { + doReturn(CONTENT_HASH_BYTES).when(mInstaller).hashSecondaryDexFile( + DEX_PATH, PACKAGE_NAME, OWNER_UID, VOLUME_UUID, STORAGE_FLAGS); + + runOnReconcile(); + + assertThat(mMessagesForUid.keySet()).containsExactly(OWNER_UID); + String expectedMessage = DEX_FILENAME_HASH + " " + CONTENT_HASH; + assertThat(mMessagesForUid).containsEntry(OWNER_UID, expectedMessage); + } + + @Test + public void testSingleAppNoFileHash() throws Exception { + doReturn(new byte[] { }).when(mInstaller).hashSecondaryDexFile( + DEX_PATH, PACKAGE_NAME, OWNER_UID, VOLUME_UUID, STORAGE_FLAGS); + + runOnReconcile(); + + assertThat(mMessagesForUid.keySet()).containsExactly(OWNER_UID); + assertThat(mMessagesForUid).containsEntry(OWNER_UID, DEX_FILENAME_HASH); + } + + @Test + public void testSingleAppHashFails() throws Exception { + doThrow(new InstallerException("Testing failure")).when(mInstaller).hashSecondaryDexFile( + DEX_PATH, PACKAGE_NAME, OWNER_UID, VOLUME_UUID, STORAGE_FLAGS); + + runOnReconcile(); + + assertThat(mMessagesForUid).isEmpty(); + } + + @Test + public void testOtherApps() throws Exception { + doReturn(CONTENT_HASH_BYTES).when(mInstaller).hashSecondaryDexFile( + DEX_PATH, PACKAGE_NAME, OWNER_UID, VOLUME_UUID, STORAGE_FLAGS); + + // Simulate three packages from two different UIDs + String packageName1 = "other1.package.name"; + String packageName2 = "other2.package.name"; + String packageName3 = "other3.package.name"; + int uid1 = 1001; + int uid2 = 1002; + + doReturn(uid1).when(mPM).getPackageUid(packageName1, 0, OWNER_USER_ID); + doReturn(uid2).when(mPM).getPackageUid(packageName2, 0, OWNER_USER_ID); + doReturn(uid1).when(mPM).getPackageUid(packageName3, 0, OWNER_USER_ID); + + runOnReconcile(packageName1, packageName2, packageName3); + + assertThat(mMessagesForUid.keySet()).containsExactly(OWNER_UID, uid1, uid2); + + String expectedMessage = DEX_FILENAME_HASH + " " + CONTENT_HASH; + assertThat(mMessagesForUid).containsEntry(OWNER_UID, expectedMessage); + assertThat(mMessagesForUid).containsEntry(uid1, expectedMessage); + assertThat(mMessagesForUid).containsEntry(uid2, expectedMessage); + } + + private void runOnReconcile(String... otherPackageNames) { + ApplicationInfo appInfo = new ApplicationInfo(); + appInfo.packageName = PACKAGE_NAME; + appInfo.volumeUuid = VOLUME_UUID; + appInfo.uid = OWNER_UID; + + boolean isUsedByOtherApps = otherPackageNames.length > 0; + DexUseInfo dexUseInfo = new DexUseInfo( + isUsedByOtherApps, OWNER_USER_ID, /* classLoaderContext */ null, /* loaderIsa */ null); + dexUseInfo.getLoadingPackages().addAll(Arrays.asList(otherPackageNames)); + + mListener.onReconcileSecondaryDexFile(appInfo, dexUseInfo, DEX_PATH, STORAGE_FLAGS); + } +} |