Fix a race in persisting instant cookie

Test: CTS passes and manual too

Change-Id: I8beb7edb84e5f7e2a181a9c237d6f0f761805d70
This commit is contained in:
Svet Ganov
2017-02-17 20:48:24 -08:00
parent ee2028c31f
commit 312c6cc6a6

View File

@@ -117,7 +117,13 @@ class InstantAppRegistry {
public byte[] getInstantAppCookieLPw(@NonNull String packageName,
@UserIdInt int userId) {
byte[] pendingCookie = mCookiePersistence.getPendingPersistCookie(userId, packageName);
// Only installed packages can get their own cookie
PackageParser.Package pkg = mService.mPackages.get(packageName);
if (pkg == null) {
return null;
}
byte[] pendingCookie = mCookiePersistence.getPendingPersistCookieLPr(pkg, userId);
if (pendingCookie != null) {
return pendingCookie;
}
@@ -144,14 +150,13 @@ class InstantAppRegistry {
}
}
// Only an installed package can set its own cookie
PackageParser.Package pkg = mService.mPackages.get(packageName);
if (pkg == null) {
return false;
}
File cookieFile = computeInstantCookieFile(pkg, userId);
mCookiePersistence.schedulePersist(userId, packageName, cookieFile, cookie);
mCookiePersistence.schedulePersistLPw(userId, pkg, cookie);
return true;
}
@@ -172,12 +177,11 @@ class InstantAppRegistry {
if (cookie == null || cookie.length <= 0) {
return;
}
try (FileOutputStream fos = new FileOutputStream(cookieFile)) {
fos.write(cookie, 0, cookie.length);
} catch (IOException e) {
Slog.e(LOG_TAG, "Error writing instant app cookie file: " + cookieFile, e);
}
}
try (FileOutputStream fos = new FileOutputStream(cookieFile)) {
fos.write(cookie, 0, cookie.length);
} catch (IOException e) {
Slog.e(LOG_TAG, "Error writing instant app cookie file: " + cookieFile, e);
}
}
@@ -242,6 +246,8 @@ class InstantAppRegistry {
if (!currentCookieFile.equals(expectedCookeFile)) {
Slog.i(LOG_TAG, "Signature for package " + pkg.packageName
+ " changed - dropping cookie");
// Make sure a pending write for the old signed app is cancelled
mCookiePersistence.cancelPendingPersistLPw(pkg, userId);
currentCookieFile.delete();
}
}
@@ -893,75 +899,88 @@ class InstantAppRegistry {
// Handler allows removing messages by id and tag where the
// tag is compared using ==. So to allow cancelling the
// pending persistence for an app under a given user we use
// the fact that package names are interned in the system
// process so the == comparison would match and we end up
// with a way to cancel persisting the cookie for a user
// and package.
private final SparseArray<ArrayMap<String, byte[]>> mPendingPersistCookies =
new SparseArray<>();
// the fact that package are cached by the system so the ==
// comparison would match and we end up with a way to cancel
// persisting the cookie for a user and package.
private final SparseArray<ArrayMap<PackageParser.Package, SomeArgs>> mPendingPersistCookies
= new SparseArray<>();
public CookiePersistence(Looper looper) {
super(looper);
}
public void schedulePersist(@UserIdInt int userId, @NonNull String packageName,
@NonNull File cookieFile, @NonNull byte[] cookie) {
cancelPendingPersist(userId, packageName);
addPendingPersistCookie(userId, packageName, cookie);
SomeArgs args = SomeArgs.obtain();
args.arg1 = packageName;
args.arg2 = cookieFile;
sendMessageDelayed(obtainMessage(userId, args),
public void schedulePersistLPw(@UserIdInt int userId, @NonNull PackageParser.Package pkg,
@NonNull byte[] cookie) {
File cookieFile = computeInstantCookieFile(pkg, userId);
cancelPendingPersistLPw(pkg, userId);
addPendingPersistCookieLPw(userId, pkg, cookie, cookieFile);
sendMessageDelayed(obtainMessage(userId, pkg),
PERSIST_COOKIE_DELAY_MILLIS);
}
public @Nullable byte[] getPendingPersistCookie(@UserIdInt int userId,
@NonNull String packageName) {
ArrayMap<String, byte[]> pendingWorkForUser = mPendingPersistCookies.get(userId);
public @Nullable byte[] getPendingPersistCookieLPr(@NonNull PackageParser.Package pkg,
@UserIdInt int userId) {
ArrayMap<PackageParser.Package, SomeArgs> pendingWorkForUser =
mPendingPersistCookies.get(userId);
if (pendingWorkForUser != null) {
return pendingWorkForUser.remove(packageName);
SomeArgs state = pendingWorkForUser.get(pkg);
if (state != null) {
return (byte[]) state.arg1;
}
}
return null;
}
private void cancelPendingPersist(@UserIdInt int userId,
@NonNull String packageName) {
removePendingPersistCookie(userId, packageName);
removeMessages(userId, packageName);
public void cancelPendingPersistLPw(@NonNull PackageParser.Package pkg,
@UserIdInt int userId) {
removeMessages(userId, pkg);
SomeArgs state = removePendingPersistCookieLPr(pkg, userId);
if (state != null) {
state.recycle();
}
}
private void addPendingPersistCookie(@UserIdInt int userId,
@NonNull String packageName, @NonNull byte[] cookie) {
ArrayMap<String, byte[]> pendingWorkForUser = mPendingPersistCookies.get(userId);
private void addPendingPersistCookieLPw(@UserIdInt int userId,
@NonNull PackageParser.Package pkg, @NonNull byte[] cookie,
@NonNull File cookieFile) {
ArrayMap<PackageParser.Package, SomeArgs> pendingWorkForUser =
mPendingPersistCookies.get(userId);
if (pendingWorkForUser == null) {
pendingWorkForUser = new ArrayMap<>();
mPendingPersistCookies.put(userId, pendingWorkForUser);
}
pendingWorkForUser.put(packageName, cookie);
SomeArgs args = SomeArgs.obtain();
args.arg1 = cookie;
args.arg2 = cookieFile;
pendingWorkForUser.put(pkg, args);
}
private byte[] removePendingPersistCookie(@UserIdInt int userId,
@NonNull String packageName) {
ArrayMap<String, byte[]> pendingWorkForUser = mPendingPersistCookies.get(userId);
byte[] cookie = null;
private SomeArgs removePendingPersistCookieLPr(@NonNull PackageParser.Package pkg,
@UserIdInt int userId) {
ArrayMap<PackageParser.Package, SomeArgs> pendingWorkForUser =
mPendingPersistCookies.get(userId);
SomeArgs state = null;
if (pendingWorkForUser != null) {
cookie = pendingWorkForUser.remove(packageName);
state = pendingWorkForUser.remove(pkg);
if (pendingWorkForUser.isEmpty()) {
mPendingPersistCookies.remove(userId);
}
}
return cookie;
return state;
}
@Override
public void handleMessage(Message message) {
int userId = message.what;
SomeArgs args = (SomeArgs) message.obj;
String packageName = (String) args.arg1;
File cookieFile = (File) args.arg2;
args.recycle();
byte[] cookie = removePendingPersistCookie(userId, packageName);
persistInstantApplicationCookie(cookie, packageName, cookieFile, userId);
PackageParser.Package pkg = (PackageParser.Package) message.obj;
SomeArgs state = removePendingPersistCookieLPr(pkg, userId);
if (state == null) {
return;
}
byte[] cookie = (byte[]) state.arg1;
File cookieFile = (File) state.arg2;
state.recycle();
persistInstantApplicationCookie(cookie, pkg.packageName, cookieFile, userId);
}
}
}