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

Commit e2b8345b authored by Atneya Nair's avatar Atneya Nair
Browse files

[appops] Add historical audio restriction logs

Add event logging for changes to audio restrictions. Refactor the data
structure to enable this and change the map to only be one level deep.
Simplify dump logic.

Test: manual dumpsys
Bug: 411153195
Flag: EXEMPT logs only
Change-Id: I47e429ce324250d98a4cc215c53a0aa4a1582bfb
parent c329df41
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -3153,7 +3153,7 @@ public class AppOpsService extends IAppOpsService.Stub {
        verifyIncomingOp(code);

        mAudioRestrictionManager.setZenModeAudioRestriction(
                code, usage, uid, mode, exceptionPackages);
                code, usage, mode, exceptionPackages);

        // Only notify default device as other devices are unaffected by restriction changes.
        mHandler.sendMessage(PooledLambda.obtainMessage(
+83 −75
Original line number Diff line number Diff line
@@ -16,16 +16,28 @@

package com.android.server.appop;

import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.MODE_ERRORED;
import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.MODE_FOREGROUND;

import android.app.AppOpsManager;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraDevice.CAMERA_AUDIO_RESTRICTION;
import android.media.AudioAttributes;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import com.android.server.utils.EventLogger;
import android.util.SparseArray;
import android.util.SparseBooleanArray;

import com.android.internal.util.Preconditions;

import java.io.PrintWriter;
import java.util.Objects;
import java.util.Set;

/**
 * AudioRestrictionManager host all audio restriction related logic and states for AppOpsService.
@@ -34,9 +46,11 @@ public class AudioRestrictionManager {
    static final String TAG = "AudioRestriction";

    // Audio restrictions coming from Zen mode API
    final SparseArray<SparseArray<Restriction>> mZenModeAudioRestrictions = new SparseArray<>();
    final ArrayMap<Key, Restriction> mZenModeAudioRestrictions = new ArrayMap<>();
    // Audio restrictions coming from Camera2 API
    @CAMERA_AUDIO_RESTRICTION int mCameraAudioRestriction = CameraDevice.AUDIO_RESTRICTION_NONE;

    final EventLogger mEventLogger = new EventLogger(/* size= */ 80, "Historical restrictions");
    // Predefined <code, usages> camera audio restriction settings
    static final SparseArray<SparseBooleanArray> CAMERA_AUDIO_RESTRICTIONS;

@@ -61,10 +75,45 @@ public class AudioRestrictionManager {
        CAMERA_AUDIO_RESTRICTIONS.append(AppOpsManager.OP_VIBRATE, vibrationMutedUsages);
    }

    private static final class Restriction {
        private static final ArraySet<String> NO_EXCEPTIONS = new ArraySet<String>();
        int mode;
        ArraySet<String> exceptionPackages = NO_EXCEPTIONS;
    private record Key(int op, int usage) {
        @Override
        public String toString() {
            return "op: "
                    + AppOpsManager.opToName(op)
                    + ", usage: "
                    + AudioAttributes.usageToString(usage);
        }
    }

    private record Restriction(int mode, Set<String> exceptionPackages) {
        Restriction {
            Objects.requireNonNull(exceptionPackages);
            // Should not be MODE_ALLOWED
            Preconditions.checkArgument(mode == MODE_IGNORED || mode == MODE_DEFAULT
                    || mode == MODE_ERRORED || mode == MODE_FOREGROUND,
                    "Invalid mode for restriction %d", mode);
        }
        @Override
        public String toString() {
            return "Restriction[mode: "
                    + AppOpsManager.modeToName(mode)
                    + ", exceptions: "
                    + exceptionPackages;
        }
    }

    private static class ZenRestrictionEvent extends EventLogger.Event {
        private Key mKey;
        private Restriction mRestriction;
        ZenRestrictionEvent(Key k, Restriction r) {
            mKey = k;
            mRestriction = r;
        }
        @Override
        public String eventToString() {
            return mKey.toString() + ": " + ((mRestriction != null) ?
                    mRestriction.toString() : "restriction removed");
        }
    }

    public int checkAudioOperation(int code, int usage, int uid, String packageName) {
@@ -77,55 +126,44 @@ public class AudioRestrictionManager {
                    final SparseBooleanArray mutedUsages = CAMERA_AUDIO_RESTRICTIONS.get(code);
                    if (mutedUsages != null) {
                        if (mutedUsages.get(usage)) {
                            return AppOpsManager.MODE_IGNORED;
                            return MODE_IGNORED;
                        }
                    }
                }
            }

            final int mode = checkZenModeRestrictionLocked(code, usage, uid, packageName);
            if (mode != AppOpsManager.MODE_ALLOWED) {
            final int mode = checkZenModeRestrictionLocked(code, usage, packageName);
            if (mode != MODE_ALLOWED) {
                return mode;
            }
        }
        return AppOpsManager.MODE_ALLOWED;
        return MODE_ALLOWED;
    }

    private int checkZenModeRestrictionLocked(int code, int usage, int uid, String packageName) {
        final SparseArray<Restriction> usageRestrictions = mZenModeAudioRestrictions.get(code);
        if (usageRestrictions != null) {
            final Restriction r = usageRestrictions.get(usage);
            if (r != null && !r.exceptionPackages.contains(packageName)) {
    private int checkZenModeRestrictionLocked(int code, int usage, String packageName) {
        final Restriction r = mZenModeAudioRestrictions.get(new Key(code, usage));
        if (r != null && (packageName == null || !r.exceptionPackages.contains(packageName))) {
                return r.mode;
        } else {
            return MODE_ALLOWED;
        }
    }
        return AppOpsManager.MODE_ALLOWED;
    }

    public void setZenModeAudioRestriction(int code, int usage, int uid, int mode,
    public void setZenModeAudioRestriction(int code, int usage, int mode,
            String[] exceptionPackages) {
        synchronized (this) {
            SparseArray<Restriction> usageRestrictions = mZenModeAudioRestrictions.get(code);
            if (usageRestrictions == null) {
                usageRestrictions = new SparseArray<Restriction>();
                mZenModeAudioRestrictions.put(code, usageRestrictions);
            }
            usageRestrictions.remove(usage);
            if (mode != AppOpsManager.MODE_ALLOWED) {
                final Restriction r = new Restriction();
                r.mode = mode;
                if (exceptionPackages != null) {
                    final int N = exceptionPackages.length;
                    r.exceptionPackages = new ArraySet<String>(N);
                    for (int i = 0; i < N; i++) {
                        final String pkg = exceptionPackages[i];
                        if (pkg != null) {
                            r.exceptionPackages.add(pkg.trim());
            final var k = new Key(code, usage);
            if (mode != MODE_ALLOWED) {
                final var r = new Restriction(mode, exceptionPackages != null ?
                                              Set.of(exceptionPackages) : Set.of());
                if (!r.equals(mZenModeAudioRestrictions.put(k, r))) {
                    mEventLogger.enqueue(new ZenRestrictionEvent(k, r));
                }
            } else {
                if (mZenModeAudioRestrictions.remove(k) != null) {
                    mEventLogger.enqueue(new ZenRestrictionEvent(k, null));
                }
            }
                usageRestrictions.put(usage, r);
            }
        }
    }

@@ -135,49 +173,19 @@ public class AudioRestrictionManager {
        }
    }

    public boolean hasActiveRestrictions() {
        boolean hasActiveRestrictions = false;
        synchronized (this) {
            hasActiveRestrictions = (mZenModeAudioRestrictions.size() > 0 ||
                mCameraAudioRestriction != CameraDevice.AUDIO_RESTRICTION_NONE);
        }
        return hasActiveRestrictions;
    }

    // return: needSep used by AppOpsService#dump
    public boolean dump(PrintWriter pw) {
        boolean printedHeader = false;
        boolean needSep = hasActiveRestrictions();

        pw.println("  Audio Restrictions:");
        synchronized (this) {
            for (int o = 0; o < mZenModeAudioRestrictions.size(); o++) {
                final String op = AppOpsManager.opToName(mZenModeAudioRestrictions.keyAt(o));
                final SparseArray<Restriction> restrictions = mZenModeAudioRestrictions.valueAt(o);
                for (int i = 0; i < restrictions.size(); i++) {
                    if (!printedHeader){
                        pw.println("  Zen Mode Audio Restrictions:");
                        printedHeader = true;

                    }
                    final int usage = restrictions.keyAt(i);
                    pw.print("    "); pw.print(op);
                    pw.print(" usage="); pw.print(AudioAttributes.usageToString(usage));
                    Restriction r = restrictions.valueAt(i);
                    pw.print(": mode="); pw.println(AppOpsManager.modeToName(r.mode));
                    if (!r.exceptionPackages.isEmpty()) {
                        pw.println("      Exceptions:");
                        for (int j = 0; j < r.exceptionPackages.size(); j++) {
                            pw.print("        "); pw.println(r.exceptionPackages.valueAt(j));
                        }
                    }
                }
            }
            if (mCameraAudioRestriction != CameraDevice.AUDIO_RESTRICTION_NONE) {
            pw.println("   Zen Audio Restriction Mode: ");
            mZenModeAudioRestrictions.forEach((k, v) -> pw.println("    " + k + ": " + v));
            pw.println("   Camera Audio Restriction Mode: " +
                    cameraRestrictionModeToName(mCameraAudioRestriction));
        }
        }
        return needSep;
        pw.println();
        mEventLogger.dump(pw, "    ");
        pw.println();
        return true;
    }

    private static String cameraRestrictionModeToName(@CAMERA_AUDIO_RESTRICTION int mode) {