Log SHA256 of secondary dex files during reconcile.
Bug: 63927552 Test: Exercised manually. Added unit test for DexManager. Change-Id: Ic8e9ea4da8eb5c22fbe088a59a406e36bc2eb6fa
This commit is contained in:
@@ -486,6 +486,16 @@ public class Installer extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] hashSecondaryDexFile(String dexPath, String packageName, int uid,
|
||||
@Nullable String volumeUuid, int flags) throws InstallerException {
|
||||
if (!checkBeforeRemote()) return new byte[0];
|
||||
try {
|
||||
return mInstalld.hashSecondaryDexFile(dexPath, packageName, uid, volumeUuid, flags);
|
||||
} catch (Exception e) {
|
||||
throw InstallerException.from(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void invalidateMounts() throws InstallerException {
|
||||
if (!checkBeforeRemote()) return;
|
||||
try {
|
||||
|
||||
@@ -293,6 +293,7 @@ import com.android.server.net.NetworkPolicyManagerInternal;
|
||||
import com.android.server.pm.Installer.InstallerException;
|
||||
import com.android.server.pm.Settings.DatabaseVersion;
|
||||
import com.android.server.pm.Settings.VersionInfo;
|
||||
import com.android.server.pm.dex.DexLogger;
|
||||
import com.android.server.pm.dex.DexManager;
|
||||
import com.android.server.pm.dex.DexoptOptions;
|
||||
import com.android.server.pm.dex.PackageDexUsage;
|
||||
@@ -2380,7 +2381,10 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
|
||||
mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
|
||||
"*dexopt*");
|
||||
mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock);
|
||||
DexManager.Listener dexManagerListener = DexLogger.getListener(this,
|
||||
installer, mInstallLock);
|
||||
mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock,
|
||||
dexManagerListener);
|
||||
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
|
||||
|
||||
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
|
||||
|
||||
116
services/core/java/com/android/server/pm/dex/DexLogger.java
Normal file
116
services/core/java/com/android/server/pm/dex/DexLogger.java
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (C) 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.os.RemoteException;
|
||||
|
||||
import android.util.ArraySet;
|
||||
import android.util.ByteStringUtils;
|
||||
import android.util.EventLog;
|
||||
import android.util.PackageUtils;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.server.pm.Installer;
|
||||
import com.android.server.pm.Installer.InstallerException;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
|
||||
|
||||
/**
|
||||
* This class is responsible for logging data about secondary dex files.
|
||||
* The data logged includes hashes of the name and content of each file.
|
||||
*/
|
||||
public class DexLogger implements DexManager.Listener {
|
||||
private static final String TAG = "DexLogger";
|
||||
|
||||
// Event log tag & subtag used for SafetyNet logging of dynamic
|
||||
// code loading (DCL) - see b/63927552.
|
||||
private static final int SNET_TAG = 0x534e4554;
|
||||
private static final String DCL_SUBTAG = "dcl";
|
||||
|
||||
private final IPackageManager mPackageManager;
|
||||
private final Object mInstallLock;
|
||||
@GuardedBy("mInstallLock")
|
||||
private final Installer mInstaller;
|
||||
|
||||
public static DexManager.Listener getListener(IPackageManager pms,
|
||||
Installer installer, Object installLock) {
|
||||
return new DexLogger(pms, installer, installLock);
|
||||
}
|
||||
|
||||
private DexLogger(IPackageManager pms, Installer installer, Object installLock) {
|
||||
mPackageManager = pms;
|
||||
mInstaller = installer;
|
||||
mInstallLock = installLock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute and log hashes of the name and content of a secondary dex file.
|
||||
*/
|
||||
@Override
|
||||
public void onReconcileSecondaryDexFile(ApplicationInfo appInfo, DexUseInfo dexUseInfo,
|
||||
String dexPath, int storageFlags) {
|
||||
int ownerUid = appInfo.uid;
|
||||
|
||||
byte[] hash = null;
|
||||
synchronized(mInstallLock) {
|
||||
try {
|
||||
hash = mInstaller.hashSecondaryDexFile(dexPath, appInfo.packageName,
|
||||
ownerUid, appInfo.volumeUuid, storageFlags);
|
||||
} catch (InstallerException e) {
|
||||
Slog.e(TAG, "Got InstallerException when hashing dex " + dexPath +
|
||||
" : " + e.getMessage());
|
||||
}
|
||||
}
|
||||
if (hash == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String dexFileName = new File(dexPath).getName();
|
||||
String message = PackageUtils.computeSha256Digest(dexFileName.getBytes());
|
||||
// Valid SHA256 will be 256 bits, 32 bytes.
|
||||
if (hash.length == 32) {
|
||||
message = message + ' ' + ByteStringUtils.toHexString(hash);
|
||||
}
|
||||
|
||||
EventLog.writeEvent(SNET_TAG, DCL_SUBTAG, ownerUid, message);
|
||||
|
||||
if (dexUseInfo.isUsedByOtherApps()) {
|
||||
Set<String> otherPackages = dexUseInfo.getLoadingPackages();
|
||||
Set<Integer> otherUids = new ArraySet<>(otherPackages.size());
|
||||
for (String otherPackageName : otherPackages) {
|
||||
try {
|
||||
int otherUid = mPackageManager.getPackageUid(
|
||||
otherPackageName, /*flags*/0, dexUseInfo.getOwnerUserId());
|
||||
if (otherUid != -1 && otherUid != ownerUid) {
|
||||
otherUids.add(otherUid);
|
||||
}
|
||||
} catch (RemoteException ignore) {
|
||||
// Can't happen, we're local.
|
||||
}
|
||||
}
|
||||
for (int otherUid : otherUids) {
|
||||
EventLog.writeEvent(SNET_TAG, DCL_SUBTAG, otherUid, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,6 +76,7 @@ public class DexManager {
|
||||
private final Object mInstallLock;
|
||||
@GuardedBy("mInstallLock")
|
||||
private final Installer mInstaller;
|
||||
private final Listener mListener;
|
||||
|
||||
// Possible outcomes of a dex search.
|
||||
private static int DEX_SEARCH_NOT_FOUND = 0; // dex file not found
|
||||
@@ -96,14 +97,24 @@ public class DexManager {
|
||||
*/
|
||||
private final static PackageUseInfo DEFAULT_USE_INFO = new PackageUseInfo();
|
||||
|
||||
public interface Listener {
|
||||
/**
|
||||
* Invoked just before the secondary dex file {@code dexPath} for the specified application
|
||||
* is reconciled.
|
||||
*/
|
||||
void onReconcileSecondaryDexFile(ApplicationInfo appInfo, DexUseInfo dexUseInfo,
|
||||
String dexPath, int storageFlags);
|
||||
}
|
||||
|
||||
public DexManager(IPackageManager pms, PackageDexOptimizer pdo,
|
||||
Installer installer, Object installLock) {
|
||||
Installer installer, Object installLock, Listener listener) {
|
||||
mPackageCodeLocationsCache = new HashMap<>();
|
||||
mPackageDexUsage = new PackageDexUsage();
|
||||
mPackageManager = pms;
|
||||
mPackageDexOptimizer = pdo;
|
||||
mInstaller = installer;
|
||||
mInstallLock = installLock;
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -389,7 +400,7 @@ public class DexManager {
|
||||
: mPackageDexOptimizer;
|
||||
String packageName = options.getPackageName();
|
||||
PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
|
||||
if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
|
||||
if (useInfo.getDexUseInfoMap().isEmpty()) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "No secondary dex use for package:" + packageName);
|
||||
}
|
||||
@@ -433,7 +444,7 @@ public class DexManager {
|
||||
*/
|
||||
public void reconcileSecondaryDexFiles(String packageName) {
|
||||
PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
|
||||
if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
|
||||
if (useInfo.getDexUseInfoMap().isEmpty()) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "No secondary dex use for package:" + packageName);
|
||||
}
|
||||
@@ -481,12 +492,16 @@ public class DexManager {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mListener != null) {
|
||||
mListener.onReconcileSecondaryDexFile(info, dexUseInfo, dexPath, flags);
|
||||
}
|
||||
|
||||
boolean dexStillExists = true;
|
||||
synchronized(mInstallLock) {
|
||||
try {
|
||||
String[] isas = dexUseInfo.getLoaderIsas().toArray(new String[0]);
|
||||
dexStillExists = mInstaller.reconcileSecondaryDexFile(dexPath, packageName,
|
||||
pkg.applicationInfo.uid, isas, pkg.applicationInfo.volumeUuid, flags);
|
||||
info.uid, isas, info.volumeUuid, flags);
|
||||
} catch (InstallerException e) {
|
||||
Slog.e(TAG, "Got InstallerException when reconciling dex " + dexPath +
|
||||
" : " + e.getMessage());
|
||||
|
||||
@@ -17,12 +17,15 @@
|
||||
package com.android.server.pm.dex;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.os.Build;
|
||||
import android.os.UserHandle;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.server.pm.Installer;
|
||||
|
||||
import dalvik.system.DelegateLastClassLoader;
|
||||
import dalvik.system.PathClassLoader;
|
||||
import dalvik.system.VMRuntime;
|
||||
@@ -36,8 +39,13 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
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 static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@@ -45,6 +53,12 @@ import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
|
||||
import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
|
||||
@@ -56,6 +70,12 @@ public class DexManagerTests {
|
||||
private static final String DELEGATE_LAST_CLASS_LOADER_NAME =
|
||||
DelegateLastClassLoader.class.getName();
|
||||
|
||||
@Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
|
||||
@Mock Installer mInstaller;
|
||||
@Mock IPackageManager mPM;
|
||||
private final Object mInstallLock = new Object();
|
||||
@Mock DexManager.Listener mListener;
|
||||
|
||||
private DexManager mDexManager;
|
||||
|
||||
private TestData mFooUser0;
|
||||
@@ -90,7 +110,8 @@ public class DexManagerTests {
|
||||
mBarUser0DelegateLastClassLoader = new TestData(bar, isa, mUser0,
|
||||
DELEGATE_LAST_CLASS_LOADER_NAME);
|
||||
|
||||
mDexManager = new DexManager(null, null, null, null);
|
||||
mDexManager = new DexManager(
|
||||
mPM, /*PackageDexOptimizer*/ null, mInstaller, mInstallLock, mListener);
|
||||
|
||||
// Foo and Bar are available to user0.
|
||||
// Only Bar is available to user1;
|
||||
@@ -440,6 +461,20 @@ public class DexManagerTests {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReconcileSecondaryDexFiles_invokesListener() throws Exception {
|
||||
List<String> fooSecondaries = mFooUser0.getSecondaryDexPathsFromProtectedDirs();
|
||||
notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
|
||||
|
||||
when(mPM.getPackageInfo(mFooUser0.getPackageName(), 0, 0))
|
||||
.thenReturn(mFooUser0.mPackageInfo);
|
||||
|
||||
mDexManager.reconcileSecondaryDexFiles(mFooUser0.getPackageName());
|
||||
|
||||
verify(mListener, times(fooSecondaries.size()))
|
||||
.onReconcileSecondaryDexFile(any(ApplicationInfo.class),
|
||||
any(DexUseInfo.class), anyString(), anyInt());
|
||||
}
|
||||
|
||||
private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
|
||||
List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
|
||||
@@ -492,12 +527,12 @@ public class DexManagerTests {
|
||||
}
|
||||
|
||||
private PackageUseInfo getPackageUseInfo(TestData testData) {
|
||||
assertTrue(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
|
||||
return mDexManager.getPackageUseInfoOrDefault(testData.mPackageInfo.packageName);
|
||||
assertTrue(mDexManager.hasInfoOnPackage(testData.getPackageName()));
|
||||
return mDexManager.getPackageUseInfoOrDefault(testData.getPackageName());
|
||||
}
|
||||
|
||||
private void assertNoUseInfo(TestData testData) {
|
||||
assertFalse(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
|
||||
assertFalse(mDexManager.hasInfoOnPackage(testData.getPackageName()));
|
||||
}
|
||||
|
||||
private static PackageInfo getMockPackageInfo(String packageName, int userId) {
|
||||
@@ -555,8 +590,8 @@ public class DexManagerTests {
|
||||
|
||||
List<String> getSecondaryDexPathsFromProtectedDirs() {
|
||||
List<String> paths = new ArrayList<>();
|
||||
paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary6.dex");
|
||||
paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary7.dex");
|
||||
paths.add(mPackageInfo.applicationInfo.deviceProtectedDataDir + "/secondary6.dex");
|
||||
paths.add(mPackageInfo.applicationInfo.credentialProtectedDataDir + "/secondary7.dex");
|
||||
return paths;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user