camera2: Fix work-profile eviction handling.

Bug: 20124384
Change-Id: I6fb82dbfd5f98746ed4befed81a583e3709bfee8
diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp
index 51a775b..a02dbe2 100644
--- a/camera/ICameraService.cpp
+++ b/camera/ICameraService.cpp
@@ -20,6 +20,7 @@
 #include <utils/Errors.h>
 #include <utils/String16.h>
 
+#include <inttypes.h>
 #include <stdint.h>
 #include <sys/types.h>
 
@@ -303,10 +304,10 @@
         return res;
     }
 
-    virtual void notifySystemEvent(int eventId, int arg0) {
+    virtual void notifySystemEvent(int32_t eventId, const int32_t* args, size_t len) {
         Parcel data, reply;
         data.writeInt32(eventId);
-        data.writeInt32(arg0);
+        data.writeInt32Array(len, args);
         remote()->transact(BnCameraService::NOTIFY_SYSTEM_EVENT, data, &reply,
                 IBinder::FLAG_ONEWAY);
     }
@@ -481,9 +482,26 @@
         } break;
         case NOTIFY_SYSTEM_EVENT: {
             CHECK_INTERFACE(ICameraService, data, reply);
-            int eventId = data.readInt32();
-            int arg0 = data.readInt32();
-            notifySystemEvent(eventId, arg0);
+            int32_t eventId = data.readInt32();
+            int32_t len = data.readInt32();
+            if (len < 0) {
+                ALOGE("%s: Received poorly formatted length in binder request: notifySystemEvent.",
+                        __FUNCTION__);
+                return FAILED_TRANSACTION;
+            }
+            if (len > 512) {
+                ALOGE("%s: Length %" PRIi32 " too long in binder request: notifySystemEvent.",
+                        __FUNCTION__, len);
+                return FAILED_TRANSACTION;
+            }
+            int32_t events[len] = {};
+            status_t status = data.read(events, sizeof(int32_t) * len);
+            if (status != NO_ERROR) {
+                ALOGE("%s: Received poorly formatted binder request: notifySystemEvent.",
+                        __FUNCTION__);
+                return FAILED_TRANSACTION;
+            }
+            notifySystemEvent(eventId, events, len);
             return NO_ERROR;
         } break;
         default:
diff --git a/include/camera/ICameraService.h b/include/camera/ICameraService.h
index cad275e..5f85635 100644
--- a/include/camera/ICameraService.h
+++ b/include/camera/ICameraService.h
@@ -164,7 +164,7 @@
     /**
      * Notify the camera service of a system event.  Should only be called from system_server.
      */
-    virtual void notifySystemEvent(int eventId, int arg0) = 0;
+    virtual void notifySystemEvent(int32_t eventId, const int32_t* args, size_t length) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 59e1c37..fc9a332 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -122,8 +122,8 @@
 // should be ok for now.
 static CameraService *gCameraService;
 
-CameraService::CameraService() : mEventLog(DEFAULT_EVENT_LOG_LENGTH),
-        mLastUserId(DEFAULT_LAST_USER_ID), mSoundRef(0), mModule(0), mFlashlight(0) {
+CameraService::CameraService() : mEventLog(DEFAULT_EVENT_LOG_LENGTH), mAllowedUsers(),
+        mSoundRef(0), mModule(0), mFlashlight(0) {
     ALOGI("CameraService started (pid=%d)", getpid());
     gCameraService = this;
 
@@ -676,6 +676,20 @@
     return NO_ERROR;
 }
 
+String8 CameraService::toString(std::set<userid_t> intSet) {
+    String8 s("");
+    bool first = true;
+    for (userid_t i : intSet) {
+        if (first) {
+            s.appendFormat("%d", i);
+            first = false;
+        } else {
+            s.appendFormat(", %d", i);
+        }
+    }
+    return s;
+}
+
 status_t CameraService::initializeShimMetadata(int cameraId) {
     int uid = getCallingUid();
 
@@ -783,7 +797,7 @@
     // Check device policy for this camera
     char value[PROPERTY_VALUE_MAX];
     char key[PROPERTY_KEY_MAX];
-    int clientUserId = multiuser_get_user_id(clientUid);
+    userid_t clientUserId = multiuser_get_user_id(clientUid);
     snprintf(key, PROPERTY_KEY_MAX, "sys.secpolicy.camera.off_%d", clientUserId);
     property_get(key, value, "0");
     if (strcmp(value, "1") == 0) {
@@ -795,10 +809,10 @@
 
     // Only allow clients who are being used by the current foreground device user, unless calling
     // from our own process.
-    if (callingPid != getpid() &&
-            (mLastUserId != clientUserId && mLastUserId != DEFAULT_LAST_USER_ID)) {
-        ALOGE("CameraService::connect X (PID %d) rejected (cannot connect from previous "
-                "device user %d, current device user %d)", callingPid, clientUserId, mLastUserId);
+    if (callingPid != getpid() && (mAllowedUsers.find(clientUserId) == mAllowedUsers.end())) {
+        ALOGE("CameraService::connect X (PID %d) rejected (cannot connect from "
+                "device user %d, currently allowed device users: %s)", callingPid, clientUserId,
+                toString(mAllowedUsers).string());
         return PERMISSION_DENIED;
     }
 
@@ -1197,10 +1211,10 @@
     return OK;
 }
 
-void CameraService::notifySystemEvent(int eventId, int arg0) {
+void CameraService::notifySystemEvent(int32_t eventId, const int32_t* args, size_t length) {
     switch(eventId) {
         case ICameraService::USER_SWITCHED: {
-            doUserSwitch(/*newUserId*/arg0);
+            doUserSwitch(/*newUserIds*/args, /*length*/length);
             break;
         }
         case ICameraService::NO_EVENT:
@@ -1443,20 +1457,30 @@
     return clientDescriptorPtr->getValue();
 }
 
-void CameraService::doUserSwitch(int newUserId) {
+void CameraService::doUserSwitch(const int32_t* newUserId, size_t length) {
     // Acquire mServiceLock and prevent other clients from connecting
     std::unique_ptr<AutoConditionLock> lock =
             AutoConditionLock::waitAndAcquire(mServiceLockWrapper);
 
-    if (newUserId <= 0) {
-        ALOGW("%s: Bad user ID %d given during user switch, resetting to default.", __FUNCTION__,
-                newUserId);
-        newUserId = DEFAULT_LAST_USER_ID;
+    std::set<userid_t> newAllowedUsers;
+    for (size_t i = 0; i < length; i++) {
+        if (newUserId[i] < 0) {
+            ALOGE("%s: Bad user ID %d given during user switch, ignoring.",
+                    __FUNCTION__, newUserId[i]);
+            return;
+        }
+        newAllowedUsers.insert(static_cast<userid_t>(newUserId[i]));
     }
 
-    logUserSwitch(mLastUserId, newUserId);
 
-    mLastUserId = newUserId;
+    if (newAllowedUsers == mAllowedUsers) {
+        ALOGW("%s: Received notification of user switch with no updated user IDs.", __FUNCTION__);
+        return;
+    }
+
+    logUserSwitch(mAllowedUsers, newAllowedUsers);
+
+    mAllowedUsers = std::move(newAllowedUsers);
 
     // Current user has switched, evict all current clients.
     std::vector<sp<BasicClient>> evicted;
@@ -1468,6 +1492,13 @@
             continue;
         }
 
+        // Don't evict clients that are still allowed.
+        uid_t clientUid = clientSp->getClientUid();
+        userid_t clientUserId = multiuser_get_user_id(clientUid);
+        if (mAllowedUsers.find(clientUserId) != mAllowedUsers.end()) {
+            continue;
+        }
+
         evicted.push_back(clientSp);
 
         String8 curTime = getFormattedCurrentTime();
@@ -1527,10 +1558,13 @@
             cameraId, clientPackage, clientPid, reason));
 }
 
-void CameraService::logUserSwitch(int oldUserId, int newUserId) {
+void CameraService::logUserSwitch(const std::set<userid_t>& oldUserIds,
+        const std::set<userid_t>& newUserIds) {
+    String8 newUsers = toString(newUserIds);
+    String8 oldUsers = toString(oldUserIds);
     // Log the new and old users
-    logEvent(String8::format("USER_SWITCH from old user: %d , to new user: %d", oldUserId,
-            newUserId));
+    logEvent(String8::format("USER_SWITCH previous allowed users: %s , current allowed users: %s",
+            oldUsers.string(), newUsers.string()));
 }
 
 void CameraService::logDeviceRemoved(const char* cameraId, const char* reason) {
@@ -1735,6 +1769,10 @@
     return mClientPid;
 }
 
+uid_t CameraService::BasicClient::getClientUid() const {
+    return mClientUid;
+}
+
 bool CameraService::BasicClient::canCastToApiClient(apiLevel level) const {
     // Defaults to API2.
     return level == API_2;
@@ -1937,12 +1975,18 @@
         auto conflicting = i->getConflicting();
         auto clientSp = i->getValue();
         String8 packageName;
+        userid_t clientUserId;
         if (clientSp.get() != nullptr) {
             packageName = String8{clientSp->getPackageName()};
+            uid_t clientUid = clientSp->getClientUid();
+            clientUserId = multiuser_get_user_id(clientUid);
         }
         ret.appendFormat("\n(Camera ID: %s, Cost: %" PRId32 ", PID: %" PRId32 ", Priority: %"
                 PRId32 ", ", key.string(), cost, pid, priority);
 
+        if (clientSp.get() != nullptr) {
+            ret.appendFormat("User Id: %d, ", clientUserId);
+        }
         if (packageName.size() != 0) {
             ret.appendFormat("Client Package Name: %s", packageName.string());
         }
@@ -2025,6 +2069,7 @@
         result.appendFormat("Number of camera devices: %d\n", mNumberOfCameras);
         String8 activeClientString = mActiveClientManager.toString();
         result.appendFormat("Active Camera Clients:\n%s", activeClientString.string());
+        result.appendFormat("Allowed users:\n%s\n", toString(mAllowedUsers).string());
 
         sp<VendorTagDescriptor> desc = VendorTagDescriptor::getGlobalVendorTagDescriptor();
         if (desc == NULL) {
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 1041550..9b7163a 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -83,11 +83,6 @@
     // Default number of messages to store in eviction log
     static const size_t DEFAULT_EVENT_LOG_LENGTH = 100;
 
-    enum {
-        // Default last user id
-        DEFAULT_LAST_USER_ID = 0,
-    };
-
     // Implementation of BinderService<T>
     static char const* getServiceName() { return "media.camera"; }
 
@@ -141,7 +136,7 @@
     virtual status_t    setTorchMode(const String16& cameraId, bool enabled,
             const sp<IBinder>& clientBinder);
 
-    virtual void notifySystemEvent(int eventId, int arg0);
+    virtual void notifySystemEvent(int32_t eventId, const int32_t* args, size_t length);
 
     // OK = supports api of that version, -EOPNOTSUPP = does not support
     virtual status_t    supportsCameraApi(
@@ -200,6 +195,9 @@
         virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode,
                 const CaptureResultExtras& resultExtras) = 0;
 
+        // Get the UID of the application client using this
+        virtual uid_t getClientUid() const;
+
         // Get the PID of the application client using this
         virtual int getClientPid() const;
 
@@ -469,7 +467,6 @@
             const String16& clientPackageName, int clientUid, apiLevel effectiveApiLevel,
             bool legacyMode, bool shimUpdateOnly, /*out*/sp<CLIENT>& device);
 
-
     // Lock guarding camera service state
     Mutex               mServiceLock;
 
@@ -492,8 +489,8 @@
     RingBuffer<String8> mEventLog;
     Mutex mLogLock;
 
-    // UID of last user.
-    int mLastUserId;
+    // Currently allowed user IDs
+    std::set<userid_t> mAllowedUsers;
 
     /**
      * Get the camera state for a given camera id.
@@ -542,7 +539,7 @@
     /**
      * Handle a notification that the current device user has changed.
      */
-    void doUserSwitch(int newUserId);
+    void doUserSwitch(const int32_t* newUserId, size_t length);
 
     /**
      * Add an event log message.
@@ -568,7 +565,8 @@
     /**
      * Add an event log message that the current device user has been switched.
      */
-    void logUserSwitch(int oldUserId, int newUserId);
+    void logUserSwitch(const std::set<userid_t>& oldUserIds,
+        const std::set<userid_t>& newUserIds);
 
     /**
      * Add an event log message that a device has been removed by the HAL
@@ -699,6 +697,11 @@
             int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode,
             int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
             /*out*/sp<BasicClient>* client);
+
+    status_t checkCameraAccess(const String16& opPackageName);
+
+    static String8 toString(std::set<userid_t> intSet);
+
 };
 
 template<class Func>
@@ -775,15 +778,6 @@
         if((ret = validateConnectLocked(cameraId, /*inout*/clientUid)) != NO_ERROR) {
             return ret;
         }
-        int userId = multiuser_get_user_id(clientUid);
-
-        if (userId != mLastUserId && clientPid != getpid() ) {
-            // If no previous user ID had been set, set to the user of the caller.
-            logUserSwitch(mLastUserId, userId);
-            LOG_ALWAYS_FATAL_IF(mLastUserId != DEFAULT_LAST_USER_ID,
-                    "Invalid state: Should never update user ID here unless was default");
-            mLastUserId = userId;
-        }
 
         // Check the shim parameters after acquiring lock, if they have already been updated and
         // we were doing a shim update, return immediately