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
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user