Skip to content
Snippets Groups Projects
Commit 5814cc70 authored by Josh Gao's avatar Josh Gao
Browse files

Purge proto tombstones when apps are uninstalled.

In preparation for making tombstones available to apps, make sure we
clean up after ourselves, so that when we recycle UIDs, we don't
accidentally give out old tombstones from unrelated previous apps.

Bug: http://b/159164105
Test: manual
Change-Id: I76646b6e98bc9019bb8d978fa416303a617a8238
parent 5968a390
No related branches found
No related tags found
No related merge requests found
......@@ -22,11 +22,16 @@ import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import android.annotation.AppIdInt;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.FileObserver;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
import android.system.ErrnoException;
import android.system.Os;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoInputStream;
......@@ -75,6 +80,9 @@ public final class NativeTombstoneManager {
}
void onSystemReady() {
registerForUserRemoval();
registerForPackageRemoval();
// Scan existing tombstones.
mHandler.post(() -> {
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 {
final ParcelFileDescriptor mPfd;
......@@ -179,6 +248,25 @@ public final class NativeTombstoneManager {
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) {
final FileInputStream is = new FileInputStream(pfd.getFileDescriptor());
final ProtoInputStream stream = new ProtoInputStream(is);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment