Merge "Purge proto tombstones when apps are uninstalled."
This commit is contained in:
@@ -22,11 +22,16 @@ import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
|
|||||||
import android.annotation.AppIdInt;
|
import android.annotation.AppIdInt;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.annotation.UserIdInt;
|
import android.annotation.UserIdInt;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
import android.os.FileObserver;
|
import android.os.FileObserver;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
|
import android.system.ErrnoException;
|
||||||
|
import android.system.Os;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.util.proto.ProtoInputStream;
|
import android.util.proto.ProtoInputStream;
|
||||||
@@ -75,6 +80,9 @@ public final class NativeTombstoneManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onSystemReady() {
|
void onSystemReady() {
|
||||||
|
registerForUserRemoval();
|
||||||
|
registerForPackageRemoval();
|
||||||
|
|
||||||
// Scan existing tombstones.
|
// Scan existing tombstones.
|
||||||
mHandler.post(() -> {
|
mHandler.post(() -> {
|
||||||
final File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
|
final File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
|
||||||
@@ -145,6 +153,67 @@ public final class NativeTombstoneManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void purge(Optional<Integer> userId, Optional<Integer> appId) {
|
||||||
|
mHandler.post(() -> {
|
||||||
|
synchronized (mLock) {
|
||||||
|
for (int i = mTombstones.size() - 1; i >= 0; --i) {
|
||||||
|
TombstoneFile tombstone = mTombstones.valueAt(i);
|
||||||
|
if (tombstone.matches(userId, appId)) {
|
||||||
|
tombstone.purge();
|
||||||
|
mTombstones.removeAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void purgePackage(int uid, boolean allUsers) {
|
||||||
|
final int appId = UserHandle.getAppId(uid);
|
||||||
|
Optional<Integer> userId;
|
||||||
|
if (allUsers) {
|
||||||
|
userId = Optional.empty();
|
||||||
|
} else {
|
||||||
|
userId = Optional.of(UserHandle.getUserId(uid));
|
||||||
|
}
|
||||||
|
purge(userId, Optional.of(appId));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void purgeUser(int uid) {
|
||||||
|
purge(Optional.of(uid), Optional.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerForPackageRemoval() {
|
||||||
|
final IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
|
||||||
|
filter.addDataScheme("package");
|
||||||
|
mContext.registerReceiverForAllUsers(new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
final int uid = intent.getIntExtra(Intent.EXTRA_UID, UserHandle.USER_NULL);
|
||||||
|
if (uid == UserHandle.USER_NULL) return;
|
||||||
|
|
||||||
|
final boolean allUsers = intent.getBooleanExtra(
|
||||||
|
Intent.EXTRA_REMOVED_FOR_ALL_USERS, false);
|
||||||
|
|
||||||
|
purgePackage(uid, allUsers);
|
||||||
|
}
|
||||||
|
}, filter, null, mHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerForUserRemoval() {
|
||||||
|
final IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction(Intent.ACTION_USER_REMOVED);
|
||||||
|
mContext.registerReceiverForAllUsers(new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
|
||||||
|
if (userId < 1) return;
|
||||||
|
|
||||||
|
purgeUser(userId);
|
||||||
|
}
|
||||||
|
}, filter, null, mHandler);
|
||||||
|
}
|
||||||
|
|
||||||
static class TombstoneFile {
|
static class TombstoneFile {
|
||||||
final ParcelFileDescriptor mPfd;
|
final ParcelFileDescriptor mPfd;
|
||||||
|
|
||||||
@@ -179,6 +248,25 @@ public final class NativeTombstoneManager {
|
|||||||
IoUtils.closeQuietly(mPfd);
|
IoUtils.closeQuietly(mPfd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void purge() {
|
||||||
|
if (!mPurged) {
|
||||||
|
// There's no way to atomically unlink a specific file for which we have an fd from
|
||||||
|
// a path, which means that we can't safely delete a tombstone without coordination
|
||||||
|
// with tombstoned (which has a risk of deadlock if for example, system_server hangs
|
||||||
|
// with a flock). Do the next best thing, and just truncate the file.
|
||||||
|
//
|
||||||
|
// We don't have to worry about inflicting a SIGBUS on a process that has the
|
||||||
|
// tombstone mmaped, because we only clear if the package has been removed, which
|
||||||
|
// means no one with access to the tombstone should be left.
|
||||||
|
try {
|
||||||
|
Os.ftruncate(mPfd.getFileDescriptor(), 0);
|
||||||
|
} catch (ErrnoException ex) {
|
||||||
|
Slog.e(TAG, "Failed to truncate tombstone", ex);
|
||||||
|
}
|
||||||
|
mPurged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Optional<TombstoneFile> parse(ParcelFileDescriptor pfd) {
|
static Optional<TombstoneFile> parse(ParcelFileDescriptor pfd) {
|
||||||
final FileInputStream is = new FileInputStream(pfd.getFileDescriptor());
|
final FileInputStream is = new FileInputStream(pfd.getFileDescriptor());
|
||||||
final ProtoInputStream stream = new ProtoInputStream(is);
|
final ProtoInputStream stream = new ProtoInputStream(is);
|
||||||
|
|||||||
Reference in New Issue
Block a user