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

Commit 62de78c2 authored by Danny Baumann's avatar Danny Baumann Committed by Scott Mertz
Browse files

Fix camera usage tracking.

When a Camera object is finalized, it notifies the torch service that
it's no longer using the camera. This call used to not check whether the
caller actually is still using the camera and called release() before,
so depending on timing of the finalizer, it could happen that e.g. the
camera app released the camera user record of the torch app, leading to
the torch usage not being detected on next usage of the camera app.

Fix that by checking whether the caller of onCameraClosed() actually
owned the camera prior to releasing the record.
Also fix synchronization between accesses.

Change-Id: Ic01cbc3c89ab8d855e4613bfa1b2f548de919b73
parent 3dc63a16
Loading
Loading
Loading
Loading
+18 −17
Original line number Diff line number Diff line
@@ -180,6 +180,7 @@ public class Camera {
    private CameraDataCallback mCameraDataCallback;
    private CameraMetaDataCallback mCameraMetaDataCallback;
    /* ### QC ADD-ONS: END */
    private Binder mTorchToken;

    /**
     * Broadcast Action:  A new picture is taken by the camera, and the entry of
@@ -329,7 +330,6 @@ public class Camera {
     * @see android.app.admin.DevicePolicyManager#getCameraDisabled(android.content.ComponentName)
     */
    public static Camera open(int cameraId) {
        notifyTorch(cameraId, true);
        return new Camera(cameraId);
    }

@@ -345,27 +345,12 @@ public class Camera {
        for (int i = 0; i < numberOfCameras; i++) {
            getCameraInfo(i, cameraInfo);
            if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
                notifyTorch(i, true);
                return new Camera(i);
            }
        }
        return null;
    }

    private static void notifyTorch(int cameraId, boolean inUse) {
        IBinder b = ServiceManager.getService(Context.TORCH_SERVICE);
        ITorchService torchService = ITorchService.Stub.asInterface(b);
        try {
            if (inUse) {
                torchService.onCameraOpened(new Binder(), cameraId);
            } else {
                torchService.onCameraClosed(cameraId);
            }
        } catch (RemoteException e) {
            // Ignore
        }
    }

    Camera(int cameraId) {
        mCameraId = cameraId;
        mShutterCallback = null;
@@ -379,6 +364,7 @@ public class Camera {
        mCameraDataCallback = null;
        mCameraMetaDataCallback = null;
        /* ### QC ADD-ONS: END */
        mTorchToken = new Binder();

        Looper looper;
        if ((looper = Looper.myLooper()) != null) {
@@ -391,6 +377,7 @@ public class Camera {

        String packageName = ActivityThread.currentPackageName();

        notifyTorch(true);
        native_setup(new WeakReference<Camera>(this), cameraId, packageName);
    }

@@ -400,6 +387,20 @@ public class Camera {
    Camera() {
    }

    private void notifyTorch(boolean inUse) {
        IBinder b = ServiceManager.getService(Context.TORCH_SERVICE);
        ITorchService torchService = ITorchService.Stub.asInterface(b);
        try {
            if (inUse) {
                torchService.onCameraOpened(mTorchToken, mCameraId);
            } else {
                torchService.onCameraClosed(mTorchToken, mCameraId);
            }
        } catch (RemoteException e) {
            // Ignore
        }
    }

    protected void finalize() {
        release();
    }
@@ -418,7 +419,7 @@ public class Camera {
    public final void release() {
        native_release();
        mFaceDetectionRunning = false;
        notifyTorch(mCameraId, false);
        notifyTorch(false);
    }

    /**
+1 −1
Original line number Diff line number Diff line
@@ -22,6 +22,6 @@ package android.hardware;
 */
interface ITorchService {
    void onCameraOpened(IBinder token, int cameraId);
    void onCameraClosed(int cameraId);
    void onCameraClosed(IBinder token, int cameraId);
    boolean onStartingTorch(int cameraId);
}
+51 −25
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ public class TorchService extends ITorchService.Stub {
    private BroadcastReceiver mStopTorchDoneReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (DEBUG) Log.d(TAG, "Torch shutdown broadcast completed");
            synchronized (mStopTorchLock) {
                mStopTorchLock.notify();
            }
@@ -57,52 +58,76 @@ public class TorchService extends ITorchService.Stub {

    @Override
    public void onCameraOpened(final IBinder token, final int cameraId) {
        if (DEBUG) Log.d(TAG, "onCameraOpened()");
        if (DEBUG) Log.d(TAG, "onCameraOpened(token= " + token + ", cameraId=" + cameraId + ")");
        boolean needTorchShutdown = false;

        synchronized (mCamerasInUse) {
            if (mTorchAppUid != 0 && Binder.getCallingUid() == mTorchAppUid) {
            if (DEBUG) Log.d(TAG, "camera was opened by torch app");
                if (DEBUG) Log.d(TAG, "Camera was opened by torch app");
                mTorchAppCameraId = cameraId;
            } else {
            if (DEBUG) Log.d(TAG, "killing torch");
                // As a synchronous broadcast is an expensive operation, only
                // attempt to kill torch if it actually grabbed the camera before
                if (cameraId == mTorchAppCameraId && mCamerasInUse.get(cameraId) != null) {
                shutdownTorch();
                    if (DEBUG) Log.d(TAG, "Need to kill torch");
                    needTorchShutdown = true;
                }
            }
        }

        // Shutdown torch outside of lock - torch shutdown will call into onCameraClosed()
        if (needTorchShutdown) {
            shutdownTorch();
        }

        try {
            token.linkToDeath(new IBinder.DeathRecipient() {
                @Override
                public void binderDied() {
                    CameraUserRecord record = mCamerasInUse.get(cameraId);
                    if (record != null && record.token == token) {
                    synchronized (mCamerasInUse) {
                        if (DEBUG) Log.d(TAG, "Camera " + cameraId + " client died");
                        mCamerasInUse.delete(cameraId);
                        removeCameraUserLocked(token, cameraId);
                    }
                }
            }, 0);
            synchronized (mCamerasInUse) {
                mCamerasInUse.put(cameraId, new CameraUserRecord(token));
            }
        } catch (RemoteException e) {
            // ignore, already dead
        }
    }

    @Override
    public void onCameraClosed(int cameraId) {
        mCamerasInUse.delete(cameraId);
        if (cameraId == mTorchAppCameraId) {
    public void onCameraClosed(final IBinder token, int cameraId) {
        if (DEBUG) Log.d(TAG, "onCameraClosed(token=" + token + ", cameraId=" + cameraId + ")");
        synchronized (mCamerasInUse) {
            removeCameraUserLocked(token, cameraId);
            if (cameraId == mTorchAppCameraId && Binder.getCallingUid() == mTorchAppUid) {
                mTorchAppCameraId = -1;
            }
        }
    }

    @Override
    public boolean onStartingTorch(int cameraId) {
        if (DEBUG) Log.d(TAG, "onStartingTorch()");
        if (DEBUG) Log.d(TAG, "onStartingTorch(cameraId=" + cameraId + ")");
        synchronized (mCamerasInUse) {
            mTorchAppUid = Binder.getCallingUid();
            if (cameraId == mTorchAppCameraId) {
                return true;
            }
            return mCamerasInUse.get(cameraId) == null;
        }
    }

    private void removeCameraUserLocked(IBinder token, int cameraId) {
        CameraUserRecord record = mCamerasInUse.get(cameraId);
        if (record != null && record.token == token) {
            if (DEBUG) Log.d(TAG, "Removing camera user " + token);
            mCamerasInUse.delete(cameraId);
        }
    }

    private void shutdownTorch() {
        // Ordered broadcasts are asynchronous (they only guarantee the order between
@@ -116,7 +141,7 @@ public class TorchService extends ITorchService.Stub {
        i.putExtra("stop", true);

        synchronized (mStopTorchLock) {
            if (DEBUG) Log.v(TAG, "sending torch shutdown broadcast");
            if (DEBUG) Log.v(TAG, "Sending torch shutdown broadcast");
            mContext.sendOrderedBroadcastAsUser(i, UserHandle.CURRENT_OR_SELF, null,
                    mStopTorchDoneReceiver, handler, Activity.RESULT_OK, null, null);

@@ -127,7 +152,7 @@ public class TorchService extends ITorchService.Stub {
            }
        }
        stopTorchThread.quit();
        if (DEBUG) Log.v(TAG, "torch shutdown completed");
        if (DEBUG) Log.v(TAG, "Torch shutdown completed");
    }

    @Override
@@ -153,5 +178,6 @@ public class TorchService extends ITorchService.Stub {
            pw.println("): pid=" + record.pid + "; package=" + TextUtils.join(",", packages));
        }
        pw.println("  mTorchAppUid=" + mTorchAppUid);
        pw.println("  mTorchAppCameraId=" + mTorchAppCameraId);
    }
}