diff options
| author | jiabin <jiabin@google.com> | 2018-04-19 16:39:44 -0700 |
|---|---|---|
| committer | jiabin <jiabin@google.com> | 2018-04-20 11:51:09 -0700 |
| commit | 503e1fdcd12d2448807d408f00a361bff0857cab (patch) | |
| tree | 26615aae29d0c02e729325d2d17932b385b675c8 /core/java/android | |
| parent | 069c5b58b09d14263b9a86852d60da45651b3099 (diff) | |
Add OP_PLAY_AUDIO to control shutter sound.
The camera will play shutter sound through native layer instead of Java
layer. That will cause shutter sound to bypass AppOps check. Add
OP_PLAY_AUDIO checking to avoid shutter sound playing while DnD.
Bug: 78136756
Test: Taking picture in Message with enable/disable DnD.
Change-Id: Iba5e7ab19cf5565ba3f074d2aba194b60e18265f
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/hardware/Camera.java | 101 |
1 files changed, 100 insertions, 1 deletions
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 9a276fbd1ec0..1b80d3d5ee57 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -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. * |
