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

Commit 878817ab authored by Atneya Nair's avatar Atneya Nair
Browse files

[audio] Add volume hardening metrics and logs

Prepare for hardening enforcement for volume APIs by logging express
metrics on volume api usage and the potential affect of levels of
hardening.

Add logging to hardening enforcer for consistency with playback
hardening.

Integrate enforcement shell cmd to test enforcement even with flags
disabled. Note, this CL does not yet include any flags, so by default no
restriction is enabled.

Test: atest AudioManagerTest
Bug: 376481063
Flag: EXEMPT logging only
Change-Id: If7bad31b121bf52d7dd2e2e33930f44f2365e8f6
parent 789cc0d0
Loading
Loading
Loading
Loading
+37 −24
Original line number Diff line number Diff line
@@ -844,23 +844,6 @@ public class AudioService extends IAudioService.Stub
                "media_audio.value_audio_playback_hardening_partial_restriction";
        static final String METRIC_COUNTERS_PLAYBACK_STRICT =
                "media_audio.value_audio_playback_hardening_strict_would_restrict";
        String getPackNameForUid(int uid) {
            final long token = Binder.clearCallingIdentity();
            try {
                final String[] names = AudioService.this.mContext.
                                            getPackageManager().getPackagesForUid(uid);
                if (names == null
                        || names.length == 0
                        || TextUtils.isEmpty(names[0])) {
                    return "[" + uid + "]";
                }
                return names[0];
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }
        // oneway
        @Override
        public void playbackHardeningEvent(int uid, byte type, boolean bypassed) {
@@ -878,7 +861,7 @@ public class AudioService extends IAudioService.Stub
            String msg = "AudioHardening background playback "
                    + (bypassed ? "would be " : "")
                    + "muted for "
                    + getPackNameForUid(uid) + " (" + uid + "), "
                    + getPackageNameForUid(uid) + " (" + uid + "), "
                    + "level: " + (type == HardeningType.PARTIAL ? "partial" : "full");
            AudioService.this.mHardeningLogger.enqueueAndSlog(msg,
@@ -1604,7 +1587,9 @@ public class AudioService extends IAudioService.Stub
        mMusicFxHelper = new MusicFxHelper(mContext, mAudioHandler);
        mHardeningEnforcer = new HardeningEnforcer(mContext, isPlatformAutomotive(), mAppOps,
        mHardeningEnforcer = new HardeningEnforcer(mContext, isPlatformAutomotive(),
                mShouldEnableAllHardening,
                mAppOps,
                context.getPackageManager(),
                mHardeningLogger);
    }
@@ -3898,7 +3883,9 @@ public class AudioService extends IAudioService.Stub
    public void adjustStreamVolumeWithAttribution(int streamType, int direction, int flags,
            String callingPackage, String attributionTag) {
        if (mHardeningEnforcer.blockVolumeMethod(
                HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_STREAM_VOLUME)) {
                HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_STREAM_VOLUME,
                callingPackage,
                Binder.getCallingUid())) {
            return;
        }
        if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) {
@@ -4659,7 +4646,9 @@ public class AudioService extends IAudioService.Stub
    public void setStreamVolumeWithAttribution(int streamType, int index, int flags,
            String callingPackage, String attributionTag) {
        if (mHardeningEnforcer.blockVolumeMethod(
                HardeningEnforcer.METHOD_AUDIO_MANAGER_SET_STREAM_VOLUME)) {
                HardeningEnforcer.METHOD_AUDIO_MANAGER_SET_STREAM_VOLUME,
                callingPackage,
                Binder.getCallingUid())) {
            return;
        }
        setStreamVolumeWithAttributionInt(streamType, index, flags, /*device*/ null,
@@ -6106,7 +6095,9 @@ public class AudioService extends IAudioService.Stub
    public void setRingerModeExternal(int ringerMode, String caller) {
        if (mHardeningEnforcer.blockVolumeMethod(
                HardeningEnforcer.METHOD_AUDIO_MANAGER_SET_RINGER_MODE)) {
                HardeningEnforcer.METHOD_AUDIO_MANAGER_SET_RINGER_MODE,
                getPackageNameForUid(Binder.getCallingUid()),
                Binder.getCallingUid())) {
            return;
        }
        if (isAndroidNPlus(caller) && wouldToggleZenMode(ringerMode)
@@ -6875,7 +6866,9 @@ public class AudioService extends IAudioService.Stub
    @Override
    public void adjustVolume(int direction, int flags) {
        if (mHardeningEnforcer.blockVolumeMethod(
                HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_VOLUME)) {
                HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_VOLUME,
                getPackageNameForUid(Binder.getCallingUid()),
                Binder.getCallingUid())) {
            return;
        }
        getMediaSessionManager().dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE,
@@ -6890,7 +6883,9 @@ public class AudioService extends IAudioService.Stub
    @Override
    public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
        if (mHardeningEnforcer.blockVolumeMethod(
                HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_SUGGESTED_STREAM_VOLUME)) {
                HardeningEnforcer.METHOD_AUDIO_MANAGER_ADJUST_SUGGESTED_STREAM_VOLUME,
                getPackageNameForUid(Binder.getCallingUid()),
                Binder.getCallingUid())) {
            return;
        }
        getMediaSessionManager().dispatchAdjustVolume(suggestedStreamType, direction, flags);
@@ -15448,4 +15443,22 @@ public class AudioService extends IAudioService.Stub
        }
        return true;
    }
    private String getPackageNameForUid(int uid) {
        final long token = Binder.clearCallingIdentity();
        try {
            final String[] names = AudioService.this.mContext.
                                        getPackageManager().getPackagesForUid(uid);
            if (names == null
                    || names.length == 0
                    || TextUtils.isEmpty(names[0])) {
                return "[" + uid + "]";
            }
            return names[0];
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }
}
+50 −12
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import com.android.modules.expresslog.Counter;
import com.android.server.utils.EventLogger;

import java.io.PrintWriter;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Class to encapsulate all audio API hardening operations
@@ -49,6 +50,7 @@ public class HardeningEnforcer {

    final Context mContext;
    final AppOpsManager mAppOps;
    final AtomicBoolean mShouldEnableAllHardening;
    final boolean mIsAutomotive;

    final ActivityManager mActivityManager;
@@ -106,10 +108,16 @@ public class HardeningEnforcer {
     */
    public static final int METHOD_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS = 300;

    public HardeningEnforcer(Context ctxt, boolean isAutomotive, AppOpsManager appOps,
            PackageManager pm, EventLogger logger) {
    private static final int ALLOWED = 0;
    private static final int DENIED_IF_PARTIAL = 1;
    private static final int DENIED_IF_FULL = 2;

    public HardeningEnforcer(Context ctxt, boolean isAutomotive,
            AtomicBoolean shouldEnableHardening, AppOpsManager appOps, PackageManager pm,
            EventLogger logger) {
        mContext = ctxt;
        mIsAutomotive = isAutomotive;
        mShouldEnableAllHardening = shouldEnableHardening;
        mAppOps = appOps;
        mActivityManager = ctxt.getSystemService(ActivityManager.class);
        mPackageManager = pm;
@@ -121,29 +129,59 @@ public class HardeningEnforcer {
     * @param volumeMethod name of the method to check, for logging purposes
     * @return false if the method call is allowed, true if it should be a no-op
     */
    protected boolean blockVolumeMethod(int volumeMethod) {
    protected boolean blockVolumeMethod(int volumeMethod, String packageName, int uid) {
        // Regardless of flag state, always permit callers with MODIFY_AUDIO_SETTINGS_PRIVILEGED
        // Prevent them from showing up in metrics as well
        if (mContext.checkCallingOrSelfPermission(
                Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
                == PackageManager.PERMISSION_GRANTED) {
            return false;
        }
        // for Auto, volume methods require MODIFY_AUDIO_SETTINGS_PRIVILEGED
        if (mIsAutomotive) {
            if (!autoPublicVolumeApiHardening()) {
                // automotive hardening flag disabled, no blocking on auto
                return false;
            }
            if (mContext.checkCallingOrSelfPermission(
                    Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
                    == PackageManager.PERMISSION_GRANTED) {
                return false;
            }
            if (Binder.getCallingUid() < UserHandle.AID_APP_START) {
            if (uid < UserHandle.AID_APP_START) {
                return false;
            }
            // TODO metrics?
            // TODO log for audio dumpsys?
            Slog.e(TAG, "Preventing volume method " + volumeMethod + " for "
                    + getPackNameForUid(Binder.getCallingUid()));
                    + packageName);
            return true;
        } else {
            int allowed;
            // No flags controlling restriction yet
            boolean enforced = mShouldEnableAllHardening.get();
            if (!noteOp(AppOpsManager.OP_CONTROL_AUDIO_PARTIAL, uid, packageName, null)) {
                // blocked by partial
                Counter.logIncrementWithUid(
                        "media_audio.value_audio_volume_hardening_partial_restriction", uid);
                allowed = DENIED_IF_PARTIAL;
            } else if (!noteOp(AppOpsManager.OP_CONTROL_AUDIO, uid, packageName, null)) {
                // blocked by full, permitted by partial
                Counter.logIncrementWithUid(
                        "media_audio.value_audio_volume_hardening_strict_restriction", uid);
                allowed = DENIED_IF_FULL;
            } else {
                // permitted with strict hardening, log anyway for API metrics
                Counter.logIncrementWithUid(
                        "media_audio.value_audio_volume_hardening_allowed", uid);
                allowed = ALLOWED;
            }
            if (allowed != ALLOWED) {
                String msg = "AudioHardening volume control for api "
                        + volumeMethod
                        + (!enforced ? " would be " : " ")
                        + "ignored for "
                        + getPackNameForUid(uid) + " (" + uid + "), "
                        + "level: " + (allowed == DENIED_IF_PARTIAL ? "partial" : "full");
                mEventLogger.enqueueAndSlog(msg, EventLogger.Event.ALOGW, TAG);
            }
            return enforced && allowed != ALLOWED;
        }
        // not blocking
        return false;
    }

    /**