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

Commit da8e229b authored by android-build-team Robot's avatar android-build-team Robot Committed by Android (Google) Code Review
Browse files

Merge "Add OP_PLAY_AUDIO to control shutter sound." into pi-dev

parents 14de2930 503e1fdc
Loading
Loading
Loading
Loading
+100 −1
Original line number Diff line number Diff line
@@ -18,19 +18,23 @@ package android.hardware;

import static android.system.OsConstants.*;

import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.ActivityThread;
import android.app.AppOpsManager;
import android.content.Context;
import android.graphics.ImageFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.media.AudioAttributes;
import android.media.IAudioService;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.renderscript.Allocation;
@@ -43,6 +47,10 @@ import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -174,6 +182,15 @@ public class Camera {
    private boolean mFaceDetectionRunning = false;
    private final Object mAutoFocusCallbackLock = new Object();

    private final Object mShutterSoundLock = new Object();
    // for AppOps
    private @Nullable IAppOpsService mAppOps;
    private IAppOpsCallback mAppOpsCallback;
    @GuardedBy("mShutterSoundLock")
    private boolean mHasAppOpsPlayAudio = true;
    @GuardedBy("mShutterSoundLock")
    private boolean mShutterSoundEnabledFromApp = true;

    private static final int NO_ERROR = 0;

    /**
@@ -526,6 +543,7 @@ public class Camera {
            // Should never hit this.
            throw new RuntimeException("Unknown camera error");
        }
        initAppOps();
    }


@@ -547,6 +565,33 @@ public class Camera {
     * An empty Camera for testing purpose.
     */
    Camera() {
        initAppOps();
    }

    private void initAppOps() {
        IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
        mAppOps = IAppOpsService.Stub.asInterface(b);
        // initialize mHasAppOpsPlayAudio
        updateAppOpsPlayAudio();
        // register a callback to monitor whether the OP_PLAY_AUDIO is still allowed
        mAppOpsCallback = new IAppOpsCallbackWrapper(this);
        try {
            mAppOps.startWatchingMode(AppOpsManager.OP_PLAY_AUDIO,
                    ActivityThread.currentPackageName(), mAppOpsCallback);
        } catch (RemoteException e) {
            Log.e(TAG, "Error registering appOps callback", e);
            mHasAppOpsPlayAudio = false;
        }
    }

    private void releaseAppOps() {
        try {
            if (mAppOps != null) {
                mAppOps.stopWatchingMode(mAppOpsCallback);
            }
        } catch (Exception e) {
            // nothing to do here, the object is supposed to be released anyway
        }
    }

    @Override
@@ -568,6 +613,7 @@ public class Camera {
    public final void release() {
        native_release();
        mFaceDetectionRunning = false;
        releaseAppOps();
    }

    /**
@@ -1623,7 +1669,17 @@ public class Camera {
                Log.e(TAG, "Audio service is unavailable for queries");
            }
        }
        return _enableShutterSound(enabled);
        synchronized (mShutterSoundLock) {
            if (enabled && mHasAppOpsPlayAudio) {
                Log.i(TAG, "Shutter sound is not allowed by AppOpsManager");
                return false;
            }
            boolean ret = _enableShutterSound(enabled);
            if (ret) {
                mShutterSoundEnabledFromApp = enabled;
            }
            return ret;
        }
    }

    /**
@@ -1648,6 +1704,49 @@ public class Camera {

    private native final boolean _enableShutterSound(boolean enabled);

    private static class IAppOpsCallbackWrapper extends IAppOpsCallback.Stub {
        private final WeakReference<Camera> mWeakCamera;

        IAppOpsCallbackWrapper(Camera camera) {
            mWeakCamera = new WeakReference<Camera>(camera);
        }

        @Override
        public void opChanged(int op, int uid, String packageName) {
            if (op == AppOpsManager.OP_PLAY_AUDIO) {
                final Camera camera = mWeakCamera.get();
                if (camera != null) {
                    camera.updateAppOpsPlayAudio();
                }
            }
        }
    }

    private void updateAppOpsPlayAudio() {
        synchronized (mShutterSoundLock) {
            boolean oldHasAppOpsPlayAudio = mHasAppOpsPlayAudio;
            try {
                int mode = AppOpsManager.MODE_IGNORED;
                if (mAppOps != null) {
                    mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
                            AudioAttributes.USAGE_ASSISTANCE_SONIFICATION,
                            Process.myUid(), ActivityThread.currentPackageName());
                }
                mHasAppOpsPlayAudio = mode == AppOpsManager.MODE_ALLOWED;
            } catch (RemoteException e) {
                Log.e(TAG, "AppOpsService check audio operation failed");
                mHasAppOpsPlayAudio = false;
            }
            if (oldHasAppOpsPlayAudio != mHasAppOpsPlayAudio) {
                if (!mHasAppOpsPlayAudio) {
                    _enableShutterSound(false);
                } else {
                    _enableShutterSound(mShutterSoundEnabledFromApp);
                }
            }
        }
    }

    /**
     * Callback interface for zoom changes during a smooth zoom operation.
     *