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

Commit 130babbc authored by Evan Severson's avatar Evan Severson Committed by Android (Google) Code Review
Browse files

Merge changes from topic "revert-22262229-revert-21960657-TEBKYKOPLW-XORRPIIXBM"

* changes:
  Revert "Revert "Only invoke for callbacks watching the given pac..."
  Revert^2 "Move appop watcher logic into AppOpsService"
  Revert "Revert "Add testing shims to allow switching of app ops/..."
parents daeae081 8ff1d001
Loading
Loading
Loading
Loading
+22 −352
Original line number Diff line number Diff line
@@ -17,32 +17,21 @@
package com.android.server.appop;

import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.OP_NONE;
import static android.app.AppOpsManager.MODE_FOREGROUND;
import static android.app.AppOpsManager.OP_SCHEDULE_EXACT_ALARM;
import static android.app.AppOpsManager.WATCH_FOREGROUND_CHANGES;
import static android.app.AppOpsManager.opRestrictsRead;
import static android.app.AppOpsManager.opToDefaultMode;

import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS;

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.AppOpsManager.Mode;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserPackage;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
@@ -53,15 +42,12 @@ import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.permission.PermissionManagerServiceInternal;

import libcore.util.EmptyArray;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

@@ -70,11 +56,8 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;


/**
@@ -128,10 +111,6 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
    @GuardedBy("mLock")
    final SparseArray<ArrayMap<String, SparseIntArray>> mUserPackageModes = new SparseArray<>();

    final SparseArray<ArraySet<OnOpModeChangedListener>> mOpModeWatchers = new SparseArray<>();
    final ArrayMap<String, ArraySet<OnOpModeChangedListener>> mPackageModeWatchers =
            new ArrayMap<>();

    final AtomicFile mFile;
    final Runnable mWriteRunner = new Runnable() {
        public void run() {
@@ -153,10 +132,6 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
    boolean mWriteScheduled;
    boolean mFastWriteScheduled;


    // Constant meaning that any UID should be matched when dispatching callbacks
    private static final int UID_ANY = -2;

    AppOpsCheckingServiceImpl(File storageFile,
            @NonNull Object lock, Handler handler, Context context,
            SparseArray<int[]> switchedOps) {
@@ -351,348 +326,43 @@ public class AppOpsCheckingServiceImpl implements AppOpsCheckingServiceInterface
    }

    @Override
    public void startWatchingOpModeChanged(@NonNull OnOpModeChangedListener changedListener,
            int op) {
        Objects.requireNonNull(changedListener);
        synchronized (mLock) {
            ArraySet<OnOpModeChangedListener> modeWatcherSet = mOpModeWatchers.get(op);
            if (modeWatcherSet == null) {
                modeWatcherSet = new ArraySet<>();
                mOpModeWatchers.put(op, modeWatcherSet);
            }
            modeWatcherSet.add(changedListener);
        }
    }

    @Override
    public void startWatchingPackageModeChanged(@NonNull OnOpModeChangedListener changedListener,
            @NonNull String packageName) {
        Objects.requireNonNull(changedListener);
        Objects.requireNonNull(packageName);
        synchronized (mLock) {
            ArraySet<OnOpModeChangedListener> modeWatcherSet =
                    mPackageModeWatchers.get(packageName);
            if (modeWatcherSet == null) {
                modeWatcherSet = new ArraySet<>();
                mPackageModeWatchers.put(packageName, modeWatcherSet);
            }
            modeWatcherSet.add(changedListener);
        }
    }

    @Override
    public void removeListener(@NonNull OnOpModeChangedListener changedListener) {
        Objects.requireNonNull(changedListener);

        synchronized (mLock) {
            for (int i = mOpModeWatchers.size() - 1; i >= 0; i--) {
                ArraySet<OnOpModeChangedListener> cbs = mOpModeWatchers.valueAt(i);
                cbs.remove(changedListener);
                if (cbs.size() <= 0) {
                    mOpModeWatchers.removeAt(i);
                }
            }

            for (int i = mPackageModeWatchers.size() - 1; i >= 0; i--) {
                ArraySet<OnOpModeChangedListener> cbs = mPackageModeWatchers.valueAt(i);
                cbs.remove(changedListener);
                if (cbs.size() <= 0) {
                    mPackageModeWatchers.removeAt(i);
                }
            }
        }
    }

    @Override
    public ArraySet<OnOpModeChangedListener> getOpModeChangedListeners(int op) {
        synchronized (mLock) {
            ArraySet<OnOpModeChangedListener> modeChangedListenersSet = mOpModeWatchers.get(op);
            if (modeChangedListenersSet == null) {
                return new ArraySet<>();
            }
            return new ArraySet<>(modeChangedListenersSet);
        }
    }

    @Override
    public ArraySet<OnOpModeChangedListener> getPackageModeChangedListeners(
            @NonNull String packageName) {
        Objects.requireNonNull(packageName);

    public SparseBooleanArray getForegroundOps(int uid) {
        SparseBooleanArray result = new SparseBooleanArray();
        synchronized (mLock) {
            ArraySet<OnOpModeChangedListener> modeChangedListenersSet =
                    mPackageModeWatchers.get(packageName);
            if (modeChangedListenersSet == null) {
                return new ArraySet<>();
            }
            return new ArraySet<>(modeChangedListenersSet);
        }
    }

    @Override
    public void notifyWatchersOfChange(int code, int uid) {
        ArraySet<OnOpModeChangedListener> listenerSet = getOpModeChangedListeners(code);
        if (listenerSet == null) {
            return;
        }
        for (int i = 0; i < listenerSet.size(); i++) {
            final OnOpModeChangedListener listener = listenerSet.valueAt(i);
            notifyOpChanged(listener, code, uid, null);
        }
    }

    @Override
    public void notifyOpChanged(@NonNull OnOpModeChangedListener onModeChangedListener, int code,
            int uid, @Nullable String packageName) {
        Objects.requireNonNull(onModeChangedListener);

        if (uid != UID_ANY && onModeChangedListener.getWatchingUid() >= 0
                && onModeChangedListener.getWatchingUid() != uid) {
            return;
        }

        // See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE
        int[] switchedCodes;
        if (onModeChangedListener.getWatchedOpCode() == ALL_OPS) {
            switchedCodes = mSwitchedOps.get(code);
        } else if (onModeChangedListener.getWatchedOpCode() == OP_NONE) {
            switchedCodes = new int[]{code};
        } else {
            switchedCodes = new int[]{onModeChangedListener.getWatchedOpCode()};
        }

        for (int switchedCode : switchedCodes) {
            // There are features watching for mode changes such as window manager
            // and location manager which are in our process. The callbacks in these
            // features may require permissions our remote caller does not have.
            final long identity = Binder.clearCallingIdentity();
            try {
                if (shouldIgnoreCallback(switchedCode, onModeChangedListener.getCallingPid(),
                        onModeChangedListener.getCallingUid())) {
                    continue;
                }
                onModeChangedListener.onOpModeChanged(switchedCode, uid, packageName);
            } catch (RemoteException e) {
                /* ignore */
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
        }
    }

    private boolean shouldIgnoreCallback(int op, int watcherPid, int watcherUid) {
        // If it's a restricted read op, ignore it if watcher doesn't have manage ops permission,
        // as watcher should not use this to signal if the value is changed.
        return opRestrictsRead(op) && mContext.checkPermission(Manifest.permission.MANAGE_APPOPS,
                watcherPid, watcherUid) != PackageManager.PERMISSION_GRANTED;
    }

    @Override
    public void notifyOpChangedForAllPkgsInUid(int code, int uid, boolean onlyForeground,
            @Nullable OnOpModeChangedListener callbackToIgnore) {
        String[] uidPackageNames = getPackagesForUid(uid);
        ArrayMap<OnOpModeChangedListener, ArraySet<String>> callbackSpecs = null;

        synchronized (mLock) {
            ArraySet<OnOpModeChangedListener> callbacks = mOpModeWatchers.get(code);
            if (callbacks != null) {
                final int callbackCount = callbacks.size();
                for (int i = 0; i < callbackCount; i++) {
                    OnOpModeChangedListener callback = callbacks.valueAt(i);

                    if (onlyForeground && (callback.getFlags()
                            & WATCH_FOREGROUND_CHANGES) == 0) {
                        continue;
                    }

                    ArraySet<String> changedPackages = new ArraySet<>();
                    Collections.addAll(changedPackages, uidPackageNames);
                    if (callbackSpecs == null) {
                        callbackSpecs = new ArrayMap<>();
                    }
                    callbackSpecs.put(callback, changedPackages);
                }
            }

            for (String uidPackageName : uidPackageNames) {
                callbacks = mPackageModeWatchers.get(uidPackageName);
                if (callbacks != null) {
                    if (callbackSpecs == null) {
                        callbackSpecs = new ArrayMap<>();
                    }
                    final int callbackCount = callbacks.size();
                    for (int i = 0; i < callbackCount; i++) {
                        OnOpModeChangedListener callback = callbacks.valueAt(i);

                        if (onlyForeground && (callback.getFlags()
                                & WATCH_FOREGROUND_CHANGES) == 0) {
                            continue;
                        }

                        ArraySet<String> changedPackages = callbackSpecs.get(callback);
                        if (changedPackages == null) {
                            changedPackages = new ArraySet<>();
                            callbackSpecs.put(callback, changedPackages);
                        }
                        changedPackages.add(uidPackageName);
                    }
                }
            }

            if (callbackSpecs != null && callbackToIgnore != null) {
                callbackSpecs.remove(callbackToIgnore);
            }
        }

        if (callbackSpecs == null) {
            return;
        }

        for (int i = 0; i < callbackSpecs.size(); i++) {
            final OnOpModeChangedListener callback = callbackSpecs.keyAt(i);
            final ArraySet<String> reportedPackageNames = callbackSpecs.valueAt(i);
            if (reportedPackageNames == null) {
                mHandler.sendMessage(PooledLambda.obtainMessage(
                        AppOpsCheckingServiceImpl::notifyOpChanged,
                        this, callback, code, uid, (String) null));

            } else {
                final int reportedPackageCount = reportedPackageNames.size();
                for (int j = 0; j < reportedPackageCount; j++) {
                    final String reportedPackageName = reportedPackageNames.valueAt(j);
                    mHandler.sendMessage(PooledLambda.obtainMessage(
                            AppOpsCheckingServiceImpl::notifyOpChanged,
                            this, callback, code, uid, reportedPackageName));
                }
            }
        }
    }

    private static String[] getPackagesForUid(int uid) {
        String[] packageNames = null;

        // Very early during boot the package manager is not yet or not yet fully started. At this
        // time there are no packages yet.
        if (AppGlobals.getPackageManager() != null) {
            try {
                packageNames = AppGlobals.getPackageManager().getPackagesForUid(uid);
            } catch (RemoteException e) {
                /* ignore - local call */
            SparseIntArray modes = mUidModes.get(uid);
            if (modes == null) {
                return result;
            }
            for (int i = 0; i < modes.size(); i++) {
                if (modes.valueAt(i) == MODE_FOREGROUND) {
                    result.put(modes.keyAt(i), true);
                }
        if (packageNames == null) {
            return EmptyArray.STRING;
            }
        return packageNames;
        }

    @Override
    public SparseBooleanArray evalForegroundUidOps(int uid, SparseBooleanArray foregroundOps) {
        synchronized (mLock) {
            return evalForegroundOps(mUidModes.get(uid), foregroundOps);
        }
        return result;
    }

    @Override
    public SparseBooleanArray evalForegroundPackageOps(String packageName,
            SparseBooleanArray foregroundOps, @UserIdInt int userId) {
    public SparseBooleanArray getForegroundOps(String packageName, int userId) {
        SparseBooleanArray result = new SparseBooleanArray();
        synchronized (mLock) {
            ArrayMap<String, SparseIntArray> packageModes = mUserPackageModes.get(userId, null);
            return evalForegroundOps(packageModes == null ? null : packageModes.get(packageName),
                    foregroundOps);
        }
    }

    private SparseBooleanArray evalForegroundOps(SparseIntArray opModes,
            SparseBooleanArray foregroundOps) {
        SparseBooleanArray tempForegroundOps = foregroundOps;
        if (opModes != null) {
            for (int i = opModes.size() - 1; i >= 0; i--) {
                if (opModes.valueAt(i) == AppOpsManager.MODE_FOREGROUND) {
                    if (tempForegroundOps == null) {
                        tempForegroundOps = new SparseBooleanArray();
                    }
                    evalForegroundWatchers(opModes.keyAt(i), tempForegroundOps);
                }
            }
        }
        return tempForegroundOps;
    }

    private void evalForegroundWatchers(int op, SparseBooleanArray foregroundOps) {
        boolean curValue = foregroundOps.get(op, false);
        ArraySet<OnOpModeChangedListener> listenerSet = mOpModeWatchers.get(op);
        if (listenerSet != null) {
            for (int cbi = listenerSet.size() - 1; !curValue && cbi >= 0; cbi--) {
                if ((listenerSet.valueAt(cbi).getFlags()
                        & AppOpsManager.WATCH_FOREGROUND_CHANGES) != 0) {
                    curValue = true;
                }
            }
        }
        foregroundOps.put(op, curValue);
    }

    @Override
    public boolean dumpListeners(int dumpOp, int dumpUid, String dumpPackage,
            PrintWriter printWriter) {
        boolean needSep = false;
        if (mOpModeWatchers.size() > 0) {
            boolean printedHeader = false;
            for (int i = 0; i < mOpModeWatchers.size(); i++) {
                if (dumpOp >= 0 && dumpOp != mOpModeWatchers.keyAt(i)) {
                    continue;
                }
                boolean printedOpHeader = false;
                ArraySet<OnOpModeChangedListener> modeChangedListenerSet =
                        mOpModeWatchers.valueAt(i);
                for (int j = 0; j < modeChangedListenerSet.size(); j++) {
                    final OnOpModeChangedListener listener = modeChangedListenerSet.valueAt(j);
                    if (dumpPackage != null
                            && dumpUid != UserHandle.getAppId(listener.getWatchingUid())) {
                        continue;
                    }
                    needSep = true;
                    if (!printedHeader) {
                        printWriter.println("  Op mode watchers:");
                        printedHeader = true;
                    }
                    if (!printedOpHeader) {
                        printWriter.print("    Op ");
                        printWriter.print(AppOpsManager.opToName(mOpModeWatchers.keyAt(i)));
                        printWriter.println(":");
                        printedOpHeader = true;
                    }
                    printWriter.print("      #"); printWriter.print(j); printWriter.print(": ");
                    printWriter.println(listener.toString());
            ArrayMap<String, SparseIntArray> packageModes = mUserPackageModes.get(userId);
            if (packageModes == null) {
                return result;
            }
            SparseIntArray modes = packageModes.get(packageName);
            if (modes == null) {
                return result;
            }
            for (int i = 0; i < modes.size(); i++) {
                if (modes.valueAt(i) == MODE_FOREGROUND) {
                    result.put(modes.keyAt(i), true);
                }

        if (mPackageModeWatchers.size() > 0 && dumpOp < 0) {
            boolean printedHeader = false;
            for (int i = 0; i < mPackageModeWatchers.size(); i++) {
                if (dumpPackage != null
                        && !dumpPackage.equals(mPackageModeWatchers.keyAt(i))) {
                    continue;
            }
                needSep = true;
                if (!printedHeader) {
                    printWriter.println("  Package mode watchers:");
                    printedHeader = true;
        }
                printWriter.print("    Pkg "); printWriter.print(mPackageModeWatchers.keyAt(i));
                printWriter.println(":");
                ArraySet<OnOpModeChangedListener> modeChangedListenerSet =
                        mPackageModeWatchers.valueAt(i);

                for (int j = 0; j < modeChangedListenerSet.size(); j++) {
                    printWriter.print("      #"); printWriter.print(j); printWriter.print(": ");
                    printWriter.println(modeChangedListenerSet.valueAt(j).toString());
                }
            }
        }
        return needSep;
        return result;
    }

    private void scheduleWriteLocked() {
+10 −95
Original line number Diff line number Diff line
@@ -16,17 +16,13 @@
package com.android.server.appop;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppOpsManager.Mode;
import android.util.ArraySet;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;

import com.android.internal.annotations.VisibleForTesting;

import java.io.PrintWriter;

/**
 * Interface for accessing and modifying modes for app-ops i.e. package and uid modes.
 * This interface also includes functions for added and removing op mode watchers.
@@ -148,99 +144,18 @@ public interface AppOpsCheckingServiceInterface {
    void clearAllModes();

    /**
     * Registers changedListener to listen to op's mode change.
     * @param changedListener the listener that must be trigger on the op's mode change.
     * @param op op representing the app-op whose mode change needs to be listened to.
     */
    void startWatchingOpModeChanged(@NonNull OnOpModeChangedListener changedListener, int op);

    /**
     * Registers changedListener to listen to package's app-op's mode change.
     * @param changedListener the listener that must be trigger on the mode change.
     * @param packageName of the package whose app-op's mode change needs to be listened to.
     */
    void startWatchingPackageModeChanged(@NonNull OnOpModeChangedListener changedListener,
            @NonNull String packageName);

    /**
     * Stop the changedListener from triggering on any mode change.
     * @param changedListener the listener that needs to be removed.
     */
    void removeListener(@NonNull OnOpModeChangedListener changedListener);

    /**
     * Temporary API which will be removed once we can safely untangle the methods that use this.
     * Returns a set of OnOpModeChangedListener that are listening for op's mode changes.
     * @param op app-op whose mode change is being listened to.
     */
    ArraySet<OnOpModeChangedListener> getOpModeChangedListeners(int op);

    /**
     * Temporary API which will be removed once we can safely untangle the methods that use this.
     * Returns a set of OnOpModeChangedListener that are listening for package's op's mode changes.
     * @param packageName of package whose app-op's mode change is being listened to.
     */
    ArraySet<OnOpModeChangedListener> getPackageModeChangedListeners(@NonNull String packageName);

    /**
     * Temporary API which will be removed once we can safely untangle the methods that use this.
     * Notify that the app-op's mode is changed by triggering the change listener.
     * @param op App-op whose mode has changed
     * @param uid user id associated with the app-op (or, if UID_ANY, notifies all users)
     */
    void notifyWatchersOfChange(int op, int uid);

    /**
     * Temporary API which will be removed once we can safely untangle the methods that use this.
     * Notify that the app-op's mode is changed by triggering the change listener.
     * @param changedListener the change listener.
     * @param op App-op whose mode has changed
     * @param uid user id associated with the app-op
     * @param packageName package name that is associated with the app-op
     * @param uid UID to query foreground ops for.
     * @return SparseBooleanArray where the keys are the op codes for which their modes are
     * MODE_FOREGROUND for the passed UID.
     */
    void notifyOpChanged(@NonNull OnOpModeChangedListener changedListener, int op, int uid,
            @Nullable String packageName);
    SparseBooleanArray getForegroundOps(int uid);

    /**
     * Temporary API which will be removed once we can safely untangle the methods that use this.
     * Notify that the app-op's mode is changed to all packages associated with the uid by
     * triggering the appropriate change listener.
     * @param op App-op whose mode has changed
     * @param uid user id associated with the app-op
     * @param onlyForeground true if only watchers that
     * @param callbackToIgnore callback that should be ignored.
     */
    void notifyOpChangedForAllPkgsInUid(int op, int uid, boolean onlyForeground,
            @Nullable OnOpModeChangedListener callbackToIgnore);

    /**
     * TODO: Move hasForegroundWatchers and foregroundOps into this.
     * Go over the list of app-ops for the uid and mark app-ops with MODE_FOREGROUND in
     * foregroundOps.
     * @param uid for which the app-op's mode needs to be marked.
     * @param foregroundOps boolean array where app-ops that have MODE_FOREGROUND are marked true.
     * @return  foregroundOps.
     */
    SparseBooleanArray evalForegroundUidOps(int uid, SparseBooleanArray foregroundOps);

    /**
     * Go over the list of app-ops for the package name and mark app-ops with MODE_FOREGROUND in
     * foregroundOps.
     * @param packageName for which the app-op's mode needs to be marked.
     * @param foregroundOps boolean array where app-ops that have MODE_FOREGROUND are marked true.
     * @param userId user id associated with the package.
     * @return foregroundOps.
     */
    SparseBooleanArray evalForegroundPackageOps(String packageName,
            SparseBooleanArray foregroundOps, @UserIdInt int userId);

    /**
     * Dump op mode and package mode listeners and their details.
     * @param dumpOp if -1 then op mode listeners for all app-ops are dumped. If it's set to an
     *               app-op, only the watchers for that app-op are dumped.
     * @param dumpUid uid for which we want to dump op mode watchers.
     * @param dumpPackage if not null and if dumpOp is -1, dumps watchers for the package name.
     * @param printWriter writer to dump to.
     *
     * @param packageName Package name to check for.
     * @param userId User ID to check for.
     * @return SparseBooleanArray where the keys are the op codes for which their modes are
     * MODE_FOREGROUND for the passed package name and user ID.
     */
    boolean dumpListeners(int dumpOp, int dumpUid, String dumpPackage, PrintWriter printWriter);
    SparseBooleanArray getForegroundOps(String packageName, int userId);
}
+6 −78

File changed.

Preview size limit exceeded, changes collapsed.

+6 −116

File changed.

Preview size limit exceeded, changes collapsed.

+7 −0
Original line number Diff line number Diff line
@@ -144,4 +144,11 @@ public interface AppOpsRestrictions {
     */
    void dumpRestrictions(PrintWriter printWriter, int dumpOp, String dumpPackage,
            boolean showUserRestrictions);

    /**
     * Listener for when an appop restriction is removed.
     */
    interface AppOpsRestrictionRemovedListener {
        void onAppOpsRestrictionRemoved(int code);
    }
}
Loading