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

Commit bf3a714a authored by Pyuli Naithani's avatar Pyuli Naithani
Browse files

Partial App-ops service refactor.

Added a new Interface for AppOpsService.
Currently this interface has funtions for accessing and modifying
app-ops modes,i.e. package and Uid modes. In future CLs we'll be moving
the callback for watching app-op mode changes to this interface as well
as op restrictions(user and global).

Added a new interface for Scheduling app-ops state persistence, so that
there is only one class for persisting the app-ops state, reducing
complexity.

This CL also includes implementation of the interface, moving the
current implementation of app-ops mode management to the impl.

BUG: 240200048

Test: Manual
Change-Id: I79a48a0afa9489b9281a6b41d92004a3f69e2e95
parent 3e420ca5
Loading
Loading
Loading
Loading
+133 −113

File changed.

Preview size limit exceeded, changes collapsed.

+98 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.server.appop;
import android.annotation.NonNull;
import android.app.AppOpsManager.Mode;
import android.util.SparseIntArray;
/**
 * Interface for accessing and modifying modes for app-ops i.e. package and uid modes.
 * In the future this interface will also include mode callbacks and op restrictions.
 */
public interface AppOpsServiceInterface {
    /**
     * Returns a copy of non-default app-ops with op as keys and their modes as values for a uid.
     * Returns an empty SparseIntArray if nothing is set.
     * @param uid for which we need the app-ops and their modes.
     */
    SparseIntArray getNonDefaultUidModes(int uid);

    /**
     * Returns the app-op mode for a particular app-op of a uid.
     * Returns default op mode if the op mode for particular uid and op is not set.
     * @param uid user id for which we need the mode.
     * @param op app-op for which we need the mode.
     * @return mode of the app-op.
     */
    int getUidMode(int uid, int op);

    /**
     * Set the app-op mode for a particular uid and op.
     * The mode is not set if the mode is the same as the default mode for the op.
     * @param uid user id for which we want to set the mode.
     * @param op app-op for which we want to set the mode.
     * @param mode mode for the app-op.
     * @return true if op mode is changed.
     */
    boolean setUidMode(int uid, int op, @Mode int mode);

    /**
     * Gets the app-op mode for a particular package.
     * Returns default op mode if the op mode for the particular package is not set.
     * @param packageName package name for which we need the op mode.
     * @param op app-op for which we need the mode.
     * @return the mode of the app-op.
     */
    int getPackageMode(@NonNull String packageName, int op);

    /**
     * Sets the app-op mode for a particular package.
     * @param packageName package name for which we need to set the op mode.
     * @param op app-op for which we need to set the mode.
     * @param mode the mode of the app-op.
     */
    void setPackageMode(@NonNull String packageName, int op, @Mode int mode);

    /**
     * Stop tracking any app-op modes for a package.
     * @param packageName Name of the package for which we want to remove all mode tracking.
     */
    boolean removePackage(@NonNull String packageName);

    /**
     * Stop tracking any app-op modes for this uid.
     * @param uid user id for which we want to remove all tracking.
     */
    void removeUid(int uid);

    /**
     * Returns true if all uid modes for this uid are
     * in default state.
     * @param uid user id
     */
    boolean areUidModesDefault(int uid);

    /**
     * Returns true if all package modes for this package name are
     * in default state.
     * @param packageName package name.
     */
    boolean arePackageModesDefault(String packageName);

    /**
     * Stop tracking app-op modes for all uid and packages.
     */
    void clearAllModes();
}
+198 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.appop;

import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.app.AppOpsManager.Mode;
import android.util.ArrayMap;
import android.util.SparseArray;
import android.util.SparseIntArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;


/**
 * Legacy implementation for App-ops service's app-op mode (uid and package) storage and access.
 * In the future this class will also include mode callbacks and op restrictions.
 */
public class LegacyAppOpsServiceInterfaceImpl implements AppOpsServiceInterface {

    // Should be the same object that the AppOpsService is using for locking.
    final Object mLock;

    @GuardedBy("mLock")
    @VisibleForTesting
    final SparseArray<SparseIntArray> mUidModes = new SparseArray<>();

    @GuardedBy("mLock")
    final ArrayMap<String, SparseIntArray> mPackageModes = new ArrayMap<>();

    final PersistenceScheduler mPersistenceScheduler;


    LegacyAppOpsServiceInterfaceImpl(PersistenceScheduler persistenceScheduler,
            @NonNull Object lock) {
        this.mPersistenceScheduler = persistenceScheduler;
        this.mLock = lock;
    }

    @Override
    public SparseIntArray getNonDefaultUidModes(int uid) {
        synchronized (mLock) {
            SparseIntArray opModes = mUidModes.get(uid, null);
            if (opModes == null) {
                return new SparseIntArray();
            }
            return opModes.clone();
        }
    }

    @Override
    public int getUidMode(int uid, int op) {
        synchronized (mLock) {
            SparseIntArray opModes = mUidModes.get(uid, null);
            if (opModes == null) {
                return AppOpsManager.opToDefaultMode(op);
            }
            return opModes.get(op, AppOpsManager.opToDefaultMode(op));
        }
    }

    @Override
    public boolean setUidMode(int uid, int op, int mode) {
        final int defaultMode = AppOpsManager.opToDefaultMode(op);
        synchronized (mLock) {
            SparseIntArray opModes = mUidModes.get(uid, null);
            if (opModes == null) {
                if (mode != defaultMode) {
                    opModes = new SparseIntArray();
                    mUidModes.put(uid, opModes);
                    opModes.put(op, mode);
                    mPersistenceScheduler.scheduleWriteLocked();
                }
            } else {
                if (opModes.indexOfKey(op) >= 0 && opModes.get(op) == mode) {
                    return false;
                }
                if (mode == defaultMode) {
                    opModes.delete(op);
                    if (opModes.size() <= 0) {
                        opModes = null;
                        mUidModes.delete(uid);
                    }
                } else {
                    opModes.put(op, mode);
                }
                mPersistenceScheduler.scheduleWriteLocked();
            }
        }
        return true;
    }

    @Override
    public int getPackageMode(String packageName, int op) {
        synchronized (mLock) {
            SparseIntArray opModes = mPackageModes.getOrDefault(packageName, null);
            if (opModes == null) {
                return AppOpsManager.opToDefaultMode(op);
            }
            return opModes.get(op, AppOpsManager.opToDefaultMode(op));
        }
    }

    @Override
    public void setPackageMode(String packageName, int op, @Mode int mode) {
        final int defaultMode = AppOpsManager.opToDefaultMode(op);
        synchronized (mLock) {
            SparseIntArray opModes = mPackageModes.get(packageName);
            if (opModes == null) {
                if (mode != defaultMode) {
                    opModes = new SparseIntArray();
                    mPackageModes.put(packageName, opModes);
                    opModes.put(op, mode);
                    mPersistenceScheduler.scheduleWriteLocked();
                }
            } else {
                if (opModes.indexOfKey(op) >= 0 && opModes.get(op) == mode) {
                    return;
                }
                if (mode == defaultMode) {
                    opModes.delete(op);
                    if (opModes.size() <= 0) {
                        opModes = null;
                        mPackageModes.remove(packageName);
                    }
                } else {
                    opModes.put(op, mode);
                }
                mPersistenceScheduler.scheduleWriteLocked();
            }
        }
    }

    @Override
    public void removeUid(int uid) {
        synchronized (mLock) {
            SparseIntArray opModes = mUidModes.get(uid);
            if (opModes == null) {
                return;
            }
            mUidModes.remove(uid);
            mPersistenceScheduler.scheduleFastWriteLocked();
        }
    }


    @Override
    public boolean areUidModesDefault(int uid) {
        synchronized (mLock) {
            SparseIntArray opModes = mUidModes.get(uid);
            return (opModes == null || opModes.size() <= 0);
        }
    }

    @Override
    public boolean arePackageModesDefault(String packageMode) {
        synchronized (mLock) {
            SparseIntArray opModes = mPackageModes.get(packageMode);
            return (opModes == null || opModes.size() <= 0);
        }
    }

    @Override
    public boolean removePackage(String packageName) {
        synchronized (mLock) {
            SparseIntArray ops = mPackageModes.remove(packageName);
            if (ops != null) {
                mPersistenceScheduler.scheduleFastWriteLocked();
                return true;
            }
            return false;
        }
    }

    @Override
    public void clearAllModes() {
        synchronized (mLock) {
            mUidModes.clear();
            mPackageModes.clear();
        }
    }

}
+35 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.appop;

/**
 * Interface that allows callers to persist AppOpsService's internal state
 * to disk.
 */
public interface PersistenceScheduler {

    /**
     * Schedules disk writes for appOpsService and it's internal states.
     */
    void scheduleWriteLocked();

    /**
     * Schedules fast disk writes for appOpsService and it's internal states.
     */
    void scheduleFastWriteLocked();

}
+5 −3
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.TypedXmlPullParser;
import android.util.Xml;

@@ -97,9 +98,10 @@ public class AppOpsUpgradeTest {
        final int defaultModeOp2 = AppOpsManager.opToDefaultMode(op2);
        for(int i = 0; i < uidStates.size(); i++) {
            final AppOpsService.UidState uidState = uidStates.valueAt(i);
            if (uidState.opModes != null) {
                final int uidMode1 = uidState.opModes.get(op1, defaultModeOp1);
                final int uidMode2 = uidState.opModes.get(op2, defaultModeOp2);
            SparseIntArray opModes = uidState.getNonDefaultUidModes();
            if (opModes != null) {
                final int uidMode1 = opModes.get(op1, defaultModeOp1);
                final int uidMode2 = opModes.get(op2, defaultModeOp2);
                assertEquals(uidMode1, uidMode2);
                if (uidMode1 != defaultModeOp1) {
                    numberOfNonDefaultOps++;