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

Commit 7f9a4c9e authored by Jayant Chowdhary's avatar Jayant Chowdhary Committed by Android (Google) Code Review
Browse files

Merge changes from topic "system-camera-cts"

* changes:
  Allow com.android.shell to get RECORD_AUDIO permissions.
  CameraManager: Add @TestApi method getCameraIdListNoLazy().
parents c022a8d1 4feeee88
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -925,6 +925,10 @@ package android.hardware.camera2 {
    field public static final int SESSION_OPERATION_MODE_VENDOR_START = 32768; // 0x8000
  }

  public final class CameraManager {
    method public String[] getCameraIdListNoLazy() throws android.hardware.camera2.CameraAccessException;
  }

}

package android.hardware.display {
+119 −27
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
import android.hardware.CameraInfo;
import android.hardware.CameraStatus;
@@ -47,6 +48,7 @@ import android.view.WindowManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
@@ -108,6 +110,21 @@ public final class CameraManager {
        return CameraManagerGlobal.get().getCameraIdList();
    }

    /**
     * Similar to getCameraIdList(). However, getCamerIdListNoLazy() necessarily communicates with
     * cameraserver in order to get the list of camera ids. This is to faciliate testing since some
     * camera ids may go 'offline' without callbacks from cameraserver because of changes in
     * SYSTEM_CAMERA permissions (though this is not a changeable permission, tests may call
     * adopt(drop)ShellPermissionIdentity() and effectively change their permissions). This call
     * affects the camera ids returned by getCameraIdList() as well. Tests which do adopt shell
     * permission identity should not mix getCameraIdList() and getCameraListNoLazyCalls().
     */
    /** @hide */
    @TestApi
    public String[] getCameraIdListNoLazy() throws CameraAccessException {
        return CameraManagerGlobal.get().getCameraIdListNoLazy();
    }

    /**
     * Register a callback to be notified about camera device availability.
     *
@@ -995,35 +1012,27 @@ public final class CameraManager {
                // Camera service is now down, leave mCameraService as null
            }
        }

        /**
         * Get a list of all camera IDs that are at least PRESENT; ignore devices that are
         * NOT_PRESENT or ENUMERATING, since they cannot be used by anyone.
         */
        public String[] getCameraIdList() {
        private String[] extractCameraIdListLocked() {
            String[] cameraIds = null;
            synchronized(mLock) {
                // Try to make sure we have an up-to-date list of camera devices.
                connectCameraServiceLocked();

            int idCount = 0;
            for (int i = 0; i < mDeviceStatus.size(); i++) {
                int status = mDeviceStatus.valueAt(i);
                    if (status == ICameraServiceListener.STATUS_NOT_PRESENT ||
                            status == ICameraServiceListener.STATUS_ENUMERATING) continue;
                if (status == ICameraServiceListener.STATUS_NOT_PRESENT
                        || status == ICameraServiceListener.STATUS_ENUMERATING) continue;
                idCount++;
            }
            cameraIds = new String[idCount];
            idCount = 0;
            for (int i = 0; i < mDeviceStatus.size(); i++) {
                int status = mDeviceStatus.valueAt(i);
                    if (status == ICameraServiceListener.STATUS_NOT_PRESENT ||
                            status == ICameraServiceListener.STATUS_ENUMERATING) continue;
                if (status == ICameraServiceListener.STATUS_NOT_PRESENT
                        || status == ICameraServiceListener.STATUS_ENUMERATING) continue;
                cameraIds[idCount] = mDeviceStatus.keyAt(i);
                idCount++;
            }
            return cameraIds;
        }

        private static void sortCameraIds(String[] cameraIds) {
            // The sort logic must match the logic in
            // libcameraservice/common/CameraProviderManager.cpp::getAPI1CompatibleCameraDeviceIds
            Arrays.sort(cameraIds, new Comparator<String>() {
@@ -1054,6 +1063,89 @@ public final class CameraManager {
                            return s1.compareTo(s2);
                        }
                    }});

        }

        public static boolean cameraStatusesContains(CameraStatus[] cameraStatuses, String id) {
            for (CameraStatus c : cameraStatuses) {
                if (c.cameraId.equals(id)) {
                    return true;
                }
            }
            return false;
        }

        public String[] getCameraIdListNoLazy() {
            CameraStatus[] cameraStatuses;
            ICameraServiceListener.Stub testListener = new ICameraServiceListener.Stub() {
                @Override
                public void onStatusChanged(int status, String id) throws RemoteException {
                }
                @Override
                public void onTorchStatusChanged(int status, String id) throws RemoteException {
                }
                @Override
                public void onCameraAccessPrioritiesChanged() {
                }};

            String[] cameraIds = null;
            synchronized (mLock) {
                connectCameraServiceLocked();
                try {
                    // The purpose of the addListener, removeListener pair here is to get a fresh
                    // list of camera ids from cameraserver. We do this since for in test processes,
                    // changes can happen w.r.t non-changeable permissions (eg: SYSTEM_CAMERA
                    // permissions can be effectively changed by calling
                    // adopt(drop)ShellPermissionIdentity()).
                    // Camera devices, which have their discovery affected by these permission
                    // changes, will not have clients get callbacks informing them about these
                    // devices going offline (in real world scenarios, these permissions aren't
                    // changeable). Future calls to getCameraIdList() will reflect the changes in
                    // the camera id list after getCameraIdListNoLazy() is called.
                    cameraStatuses = mCameraService.addListener(testListener);
                    mCameraService.removeListener(testListener);
                    for (CameraStatus c : cameraStatuses) {
                        onStatusChangedLocked(c.status, c.cameraId);
                    }
                    Set<String> deviceCameraIds = mDeviceStatus.keySet();
                    ArrayList<String> deviceIdsToRemove = new ArrayList<String>();
                    for (String deviceCameraId : deviceCameraIds) {
                        // Its possible that a device id was removed without a callback notifying
                        // us. This may happen in case a process 'drops' system camera permissions
                        // (even though the permission isn't a changeable one, tests may call
                        // adoptShellPermissionIdentity() and then dropShellPermissionIdentity().
                        if (!cameraStatusesContains(cameraStatuses, deviceCameraId)) {
                            deviceIdsToRemove.add(deviceCameraId);
                        }
                    }
                    for (String id : deviceIdsToRemove) {
                        onStatusChangedLocked(ICameraServiceListener.STATUS_NOT_PRESENT, id);
                    }
                } catch (ServiceSpecificException e) {
                    // Unexpected failure
                    throw new IllegalStateException("Failed to register a camera service listener",
                            e);
                } catch (RemoteException e) {
                    // Camera service is now down, leave mCameraService as null
                }
                cameraIds = extractCameraIdListLocked();
            }
            sortCameraIds(cameraIds);
            return cameraIds;
        }

        /**
         * Get a list of all camera IDs that are at least PRESENT; ignore devices that are
         * NOT_PRESENT or ENUMERATING, since they cannot be used by anyone.
         */
        public String[] getCameraIdList() {
            String[] cameraIds = null;
            synchronized (mLock) {
                // Try to make sure we have an up-to-date list of camera devices.
                connectCameraServiceLocked();
                cameraIds = extractCameraIdListLocked();
            }
            sortCameraIds(cameraIds);
            return cameraIds;
        }

+2 −0
Original line number Diff line number Diff line
@@ -172,6 +172,8 @@
    <!-- Permissions needed to test system only camera devices -->
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.SYSTEM_CAMERA" />
    <!-- Permissions needed for CTS camera test: RecordingTest.java when assuming shell id -->
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <!-- Permission needed to enable/disable Bluetooth/Wifi -->
    <uses-permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED" />
    <uses-permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED" />