Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 312c6cc6 authored by Svet Ganov's avatar Svet Ganov
Browse files

Fix a race in persisting instant cookie

Test: CTS passes and manual too

Change-Id: I8beb7edb84e5f7e2a181a9c237d6f0f761805d70
parent ee2028c3
Loading
Loading
Loading
Loading
+67 −48
Original line number Diff line number Diff line
@@ -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,14 +177,13 @@ 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);
        }
    }
    }

    public Bitmap getInstantAppIconLPw(@NonNull String packageName,
                                       @UserIdInt int userId) {
@@ -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);
        }
    }
}