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

Commit 4e3b435c authored by Winson's avatar Winson Committed by Winson Chiu
Browse files

DO NOT MERGE: Delete persisted historical app ops on package uninstall

They're removed from the current state, but not the persisted state.

This adds HistoricalRegistry#clearHistoryForPackage which reads the
disk state, strips the corresponding UID/package, and re-writes
to disk.

Bug: 129796626

Test: manual test app with location access
Test: atest AppOpsServiceTest#testPackageRemovedHistoricalOps

Change-Id: I8daa2e3474b400a3789b2eaf178441c6d1578af1
parent 8197beee
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -3119,6 +3119,15 @@ public class AppOpsManager {
            return mHistoricalUidOps.get(uid);
        }

        /** @hide */
        public void clearHistory(int uid, @NonNull String packageName) {
            HistoricalUidOps historicalUidOps = getOrCreateHistoricalUidOps(uid);
            historicalUidOps.clearHistory(packageName);
            if (historicalUidOps.isEmpty()) {
                mHistoricalUidOps.remove(uid);
            }
        }

        @Override
        public int describeContents() {
            return 0;
@@ -3396,6 +3405,12 @@ public class AppOpsManager {
            return mHistoricalPackageOps.get(packageName);
        }

        private void clearHistory(@NonNull String packageName) {
            if (mHistoricalPackageOps != null) {
                mHistoricalPackageOps.remove(packageName);
            }
        }

        @Override
        public int describeContents() {
            return 0;
+2 −0
Original line number Diff line number Diff line
@@ -905,6 +905,8 @@ public class AppOpsService extends IAppOpsService.Stub {
                    }
                }
            }

            mHistoricalRegistry.clearHistory(uid, packageName);
        }
    }

+35 −0
Original line number Diff line number Diff line
@@ -472,6 +472,25 @@ final class HistoricalRegistry {
                DEFAULT_COMPRESSION_STEP);
    }

    void clearHistory(int uid, String packageName) {
        synchronized (mOnDiskLock) {
            synchronized (mInMemoryLock) {
                if (mMode != AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
                    return;
                }

                for (int index = 0; index < mPendingWrites.size(); index++) {
                    mPendingWrites.get(index).clearHistory(uid, packageName);
                }

                getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis())
                        .clearHistory(uid, packageName);

                mPersistence.clearHistoryDLocked(uid, packageName);
            }
        }
    }

    void clearHistory() {
        synchronized (mOnDiskLock) {
            clearHistoryOnDiskLocked();
@@ -628,6 +647,22 @@ final class HistoricalRegistry {
            return new File(baseDir, Long.toString(globalBeginMillis) + HISTORY_FILE_SUFFIX);
        }

        void clearHistoryDLocked(int uid, String packageName) {
            List<HistoricalOps> historicalOps = readHistoryDLocked();

            if (historicalOps == null) {
                return;
            }

            for (int index = 0; index < historicalOps.size(); index++) {
                historicalOps.get(index).clearHistory(uid, packageName);
            }

            clearHistoryDLocked();

            persistHistoricalOpsDLocked(historicalOps);
        }

        void clearHistoryDLocked() {
            mHistoricalAppOpsDir.delete();
        }
+1 −0
Original line number Diff line number Diff line
@@ -69,6 +69,7 @@
    <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
    <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" />
    <uses-permission android:name="android.permission.HARDWARE_TEST"/>
    <uses-permission android:name="android.permission.MANAGE_APPOPS"/>

    <!-- Uses API introduced in O (26) -->
    <uses-sdk android:minSdkVersion="1"
+44 −2
Original line number Diff line number Diff line
@@ -29,25 +29,28 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;

import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.AppOpsManager.OpEntry;
import android.app.AppOpsManager.PackageOps;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
import android.os.RemoteCallback;

import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.server.appop.AppOpsService;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.io.File;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Unit tests for AppOpsService. Covers functionality that is difficult to test using CTS tests
@@ -215,6 +218,45 @@ public class AppOpsServiceTest {
        assertThat(getLoggedOps()).isNull();
    }

    @Test
    public void testPackageRemovedHistoricalOps() throws InterruptedException {
        mAppOpsService.setMode(OP_READ_SMS, mMyUid, mMyPackageName, MODE_ALLOWED);
        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, mMyPackageName);

        AppOpsManager.HistoricalOps historicalOps = new AppOpsManager.HistoricalOps(0, 15000);
        historicalOps.increaseAccessCount(OP_READ_SMS, mMyUid, mMyPackageName,
                AppOpsManager.UID_STATE_PERSISTENT, 0, 1);

        mAppOpsService.addHistoricalOps(historicalOps);

        AtomicReference<AppOpsManager.HistoricalOps> resultOpsRef = new AtomicReference<>();
        AtomicReference<CountDownLatch> latchRef = new AtomicReference<>(new CountDownLatch(1));
        RemoteCallback callback = new RemoteCallback(result -> {
            resultOpsRef.set(result.getParcelable(AppOpsManager.KEY_HISTORICAL_OPS));
            latchRef.get().countDown();
        });

        // First, do a fetch to ensure it's written
        mAppOpsService.getHistoricalOps(mMyUid, mMyPackageName, null, 0, Long.MAX_VALUE, 0,
                callback);

        latchRef.get().await(5, TimeUnit.SECONDS);
        assertThat(latchRef.get().getCount()).isEqualTo(0);
        assertThat(resultOpsRef.get().isEmpty()).isFalse();

        // Then, check it's deleted on removal
        mAppOpsService.packageRemoved(mMyUid, mMyPackageName);

        latchRef.set(new CountDownLatch(1));

        mAppOpsService.getHistoricalOps(mMyUid, mMyPackageName, null, 0, Long.MAX_VALUE, 0,
                callback);

        latchRef.get().await(5, TimeUnit.SECONDS);
        assertThat(latchRef.get().getCount()).isEqualTo(0);
        assertThat(resultOpsRef.get().isEmpty()).isTrue();
    }

    @Test
    public void testUidRemoved() {
        mAppOpsService.setMode(OP_READ_SMS, mMyUid, mMyPackageName, MODE_ALLOWED);