No sensor access to idle UIDs - native framework

Idle UIDs are ones that were in the background for long enough time.
Currently such apps can access sensor data even though they have no
user perceptible components running. This affects the user's privacy
since an app in the background can use sensor data to infer location,
activity, habits, etc.

The goal is to restrict sensor access for all apps in the ecosystem
regardless of target SDK which means the solution should be backwards
compatible. At the high level the sesnor service observes UID state
changes and applies policy like this:

Continuous sensors: for sensros in this reporting mode when the UID
goes in the background we will stop dispatching events. Once the UID
goes active we will start reporting the events. While this is an
app visible behavior change we would rather do that vs delivering
fake events.

Flush events: there is no change in behavior based on the UID state.
Hence, idle apps can request a flush and would get the completion
callback. From an app perspective flushing works at any point.

Trigger events: for sensors in this reporting mode when the UID
goes in the background we will not report any trigger events. From
an app perspective the sensor just did not pick up any events.

On-change events: for sensors in this reporting mode when the UID
goes in the background we will not report any change events. From
an app perspective the sensor just did not pick up any events.

Wake locks: since UIDs in idle state cannot acquire wakelocks we
will not be grabbing a wakelock on behalf of apps in that state.

Test: Added - SensorTest#testSanitizedContinuousEventsUidIdle
      Added - SensorTest#testBatchAndFlushUidIdle
      Pass - cts-tradefed run cts-dev -m CtsSensorTestCases

bug:63938985

Change-Id: I156803610ad6d86afaae641ebbb0e84f16d2344b
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 0a05dd1..956844f 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -29,11 +29,11 @@
 
 SensorService::SensorEventConnection::SensorEventConnection(
         const sp<SensorService>& service, uid_t uid, String8 packageName, bool isDataInjectionMode,
-        const String16& opPackageName)
+        const String16& opPackageName, bool hasSensorAccess)
     : mService(service), mUid(uid), mWakeLockRefCount(0), mHasLooperCallbacks(false),
       mDead(false), mDataInjectionMode(isDataInjectionMode), mEventCache(NULL),
       mCacheSize(0), mMaxCacheSize(0), mPackageName(packageName), mOpPackageName(opPackageName),
-      mDestroyed(false) {
+      mDestroyed(false), mHasSensorAccess(hasSensorAccess) {
     mChannel = new BitTube(mService->mSocketBufferSize);
 #if DEBUG_CONNECTIONS
     mEventsReceived = mEventsSentFromCache = mEventsSent = 0;
@@ -223,6 +223,9 @@
         sensors_event_t* scratch,
         wp<const SensorEventConnection> const * mapFlushEventsToConnections) {
     // filter out events not for this connection
+
+    sensors_event_t* sanitizedBuffer = nullptr;
+
     int count = 0;
     Mutex::Autolock _l(mConnectionLock);
     if (scratch) {
@@ -273,24 +276,36 @@
                     if (mapFlushEventsToConnections[i] == this) {
                         scratch[count++] = buffer[i];
                     }
-                    ++i;
                 } else {
                     // Regular sensor event, just copy it to the scratch buffer.
-                    scratch[count++] = buffer[i++];
+                    if (mHasSensorAccess) {
+                        scratch[count++] = buffer[i];
+                    }
                 }
+                i++;
             } while ((i<numEvents) && ((buffer[i].sensor == sensor_handle &&
                                         buffer[i].type != SENSOR_TYPE_META_DATA) ||
                                        (buffer[i].type == SENSOR_TYPE_META_DATA  &&
                                         buffer[i].meta_data.sensor == sensor_handle)));
         }
     } else {
-        scratch = const_cast<sensors_event_t *>(buffer);
-        count = numEvents;
+        if (mHasSensorAccess) {
+            scratch = const_cast<sensors_event_t *>(buffer);
+            count = numEvents;
+        } else {
+            scratch = sanitizedBuffer = new sensors_event_t[numEvents];
+            for (size_t i = 0; i < numEvents; i++) {
+                if (buffer[i].type == SENSOR_TYPE_META_DATA) {
+                    scratch[count++] = buffer[i++];
+                }
+            }
+        }
     }
 
     sendPendingFlushEventsLocked();
     // Early return if there are no events for this connection.
     if (count == 0) {
+        delete sanitizedBuffer;
         return status_t(NO_ERROR);
     }
 
@@ -308,6 +323,7 @@
             // the max cache size that is desired.
             if (mCacheSize + count < computeMaxCacheSizeLocked()) {
                 reAllocateCacheLocked(scratch, count);
+                delete sanitizedBuffer;
                 return status_t(NO_ERROR);
             }
             // Some events need to be dropped.
@@ -326,16 +342,20 @@
             memcpy(&mEventCache[mCacheSize - numEventsDropped], scratch + remaningCacheSize,
                                             numEventsDropped * sizeof(sensors_event_t));
         }
+        delete sanitizedBuffer;
         return status_t(NO_ERROR);
     }
 
-    int index_wake_up_event = findWakeUpSensorEventLocked(scratch, count);
-    if (index_wake_up_event >= 0) {
-        scratch[index_wake_up_event].flags |= WAKE_UP_SENSOR_EVENT_NEEDS_ACK;
-        ++mWakeLockRefCount;
+    int index_wake_up_event = -1;
+    if (mHasSensorAccess) {
+        index_wake_up_event = findWakeUpSensorEventLocked(scratch, count);
+        if (index_wake_up_event >= 0) {
+            scratch[index_wake_up_event].flags |= WAKE_UP_SENSOR_EVENT_NEEDS_ACK;
+            ++mWakeLockRefCount;
 #if DEBUG_CONNECTIONS
-        ++mTotalAcksNeeded;
+            ++mTotalAcksNeeded;
 #endif
+        }
     }
 
     // NOTE: ASensorEvent and sensors_event_t are the same type.
@@ -364,6 +384,7 @@
         // Add this file descriptor to the looper to get a callback when this fd is available for
         // writing.
         updateLooperRegistrationLocked(mService->getLooper());
+        delete sanitizedBuffer;
         return size;
     }
 
@@ -373,9 +394,15 @@
     }
 #endif
 
+    delete sanitizedBuffer;
     return size < 0 ? status_t(size) : status_t(NO_ERROR);
 }
 
+void SensorService::SensorEventConnection::setSensorAccess(const bool hasAccess) {
+    Mutex::Autolock _l(mConnectionLock);
+    mHasSensorAccess = hasAccess;
+}
+
 void SensorService::SensorEventConnection::reAllocateCacheLocked(sensors_event_t const* scratch,
                                                                  int count) {
     sensors_event_t *eventCache_new;
@@ -437,15 +464,18 @@
     sendPendingFlushEventsLocked();
     for (int numEventsSent = 0; numEventsSent < mCacheSize;) {
         const int numEventsToWrite = helpers::min(mCacheSize - numEventsSent, maxWriteSize);
-        int index_wake_up_event =
-                  findWakeUpSensorEventLocked(mEventCache + numEventsSent, numEventsToWrite);
-        if (index_wake_up_event >= 0) {
-            mEventCache[index_wake_up_event + numEventsSent].flags |=
-                    WAKE_UP_SENSOR_EVENT_NEEDS_ACK;
-            ++mWakeLockRefCount;
+        int index_wake_up_event = -1;
+        if (mHasSensorAccess) {
+            index_wake_up_event =
+                      findWakeUpSensorEventLocked(mEventCache + numEventsSent, numEventsToWrite);
+            if (index_wake_up_event >= 0) {
+                mEventCache[index_wake_up_event + numEventsSent].flags |=
+                        WAKE_UP_SENSOR_EVENT_NEEDS_ACK;
+                ++mWakeLockRefCount;
 #if DEBUG_CONNECTIONS
-            ++mTotalAcksNeeded;
+                ++mTotalAcksNeeded;
 #endif
+            }
         }
 
         ssize_t size = SensorEventQueue::write(mChannel,
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 6f282cd..032721e 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -49,7 +49,8 @@
 
 public:
     SensorEventConnection(const sp<SensorService>& service, uid_t uid, String8 packageName,
-                          bool isDataInjectionMode, const String16& opPackageName);
+                          bool isDataInjectionMode, const String16& opPackageName,
+                          bool hasSensorAccess);
 
     status_t sendEvents(sensors_event_t const* buffer, size_t count, sensors_event_t* scratch,
                         wp<const SensorEventConnection> const * mapFlushEventsToConnections = NULL);
@@ -66,6 +67,8 @@
 
     uid_t getUid() const { return mUid; }
 
+    void setSensorAccess(const bool hasAccess);
+
 private:
     virtual ~SensorEventConnection();
     virtual void onFirstRef();
@@ -167,6 +170,7 @@
 
     mutable Mutex mDestroyLock;
     bool mDestroyed;
+    bool mHasSensorAccess;
 };
 
 } // namepsace android
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index dc491d9..c32ffb9 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -13,11 +13,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include <binder/ActivityManager.h>
 #include <binder/AppOpsManager.h>
 #include <binder/BinderService.h>
 #include <binder/IServiceManager.h>
 #include <binder/PermissionCache.h>
+#include <binder/PermissionController.h>
 #include <cutils/ashmem.h>
+#include <cutils/misc.h>
 #include <cutils/properties.h>
 #include <hardware/sensors.h>
 #include <hardware_legacy/power.h>
@@ -52,6 +55,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <private/android_filesystem_config.h>
+
 namespace android {
 // ---------------------------------------------------------------------------
 
@@ -75,6 +80,7 @@
 // Permissions.
 static const String16 sDumpPermission("android.permission.DUMP");
 static const String16 sLocationHardwarePermission("android.permission.LOCATION_HARDWARE");
+static const String16 sManageSensorsPermission("android.permission.MANAGE_SENSORS");
 
 SensorService::SensorService()
     : mInitCheck(NO_INIT), mSocketBufferSize(SOCKET_BUFFER_SIZE_NON_BATCHED),
@@ -274,6 +280,23 @@
 
             // priority can only be changed after run
             enableSchedFifoMode();
+
+            // Start watching UID changes to apply policy.
+            mUidPolicy = new UidPolicy(this);
+            mUidPolicy->registerSelf();
+        }
+    }
+}
+
+void SensorService::setSensorAccess(uid_t uid, bool hasAccess) {
+    SortedVector< sp<SensorEventConnection> > activeConnections;
+    populateActiveConnections(&activeConnections);
+    {
+        Mutex::Autolock _l(mLock);
+        for (size_t i = 0 ; i < activeConnections.size(); i++) {
+            if (activeConnections[i] != 0 && activeConnections[i]->getUid() == uid) {
+                activeConnections[i]->setSensorAccess(hasAccess);
+            }
         }
     }
 }
@@ -312,6 +335,7 @@
     for (auto && entry : mRecentEvent) {
         delete entry.second;
     }
+    mUidPolicy->unregisterSelf();
 }
 
 status_t SensorService::dump(int fd, const Vector<String16>& args) {
@@ -488,6 +512,82 @@
     return NO_ERROR;
 }
 
+// NOTE: This is a remote API - make sure all args are validated
+status_t SensorService::shellCommand(int in, int out, int err, Vector<String16>& args) {
+    if (!checkCallingPermission(sManageSensorsPermission, nullptr, nullptr)) {
+        return PERMISSION_DENIED;
+    }
+    if (in == BAD_TYPE || out == BAD_TYPE || err == BAD_TYPE) {
+        return BAD_VALUE;
+    }
+    if (args.size() == 3 && args[0] == String16("set-uid-state")) {
+        return handleSetUidState(args, err);
+    } else if (args.size() == 2 && args[0] == String16("reset-uid-state")) {
+        return handleResetUidState(args, err);
+    } else if (args.size() == 2 && args[0] == String16("get-uid-state")) {
+        return handleGetUidState(args, out, err);
+    } else if (args.size() == 1 && args[0] == String16("help")) {
+        printHelp(out);
+        return NO_ERROR;
+    }
+    printHelp(err);
+    return BAD_VALUE;
+}
+
+status_t SensorService::handleSetUidState(Vector<String16>& args, int err) {
+    PermissionController pc;
+    int uid = pc.getPackageUid(args[1], 0);
+    if (uid <= 0) {
+        ALOGE("Unknown package: '%s'", String8(args[1]).string());
+        dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string());
+        return BAD_VALUE;
+    }
+    bool active = false;
+    if (args[2] == String16("active")) {
+        active = true;
+    } else if ((args[2] != String16("idle"))) {
+        ALOGE("Expected active or idle but got: '%s'", String8(args[2]).string());
+        return BAD_VALUE;
+    }
+    mUidPolicy->addOverrideUid(uid, active);
+    return NO_ERROR;
+}
+
+status_t SensorService::handleResetUidState(Vector<String16>& args, int err) {
+    PermissionController pc;
+    int uid = pc.getPackageUid(args[1], 0);
+    if (uid < 0) {
+        ALOGE("Unknown package: '%s'", String8(args[1]).string());
+        dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string());
+        return BAD_VALUE;
+    }
+    mUidPolicy->removeOverrideUid(uid);
+    return NO_ERROR;
+}
+
+status_t SensorService::handleGetUidState(Vector<String16>& args, int out, int err) {
+    PermissionController pc;
+    int uid = pc.getPackageUid(args[1], 0);
+    if (uid < 0) {
+        ALOGE("Unknown package: '%s'", String8(args[1]).string());
+        dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string());
+        return BAD_VALUE;
+    }
+    if (mUidPolicy->isUidActive(uid)) {
+        return dprintf(out, "active\n");
+    } else {
+        return dprintf(out, "idle\n");
+    }
+}
+
+status_t SensorService::printHelp(int out) {
+    return dprintf(out, "Sensor service commands:\n"
+        "  get-uid-state <PACKAGE> gets the uid state\n"
+        "  set-uid-state <PACKAGE> <active|idle> overrides the uid state\n"
+        "  reset-uid-state <PACKAGE> clears the uid state override\n"
+        "  help print this message\n");
+}
+
 //TODO: move to SensorEventConnection later
 void SensorService::cleanupAutoDisabledSensorLocked(const sp<SensorEventConnection>& connection,
         sensors_event_t const* buffer, const int count) {
@@ -677,7 +777,6 @@
             }
         }
 
-
         // Send our events to clients. Check the state of wake lock for each client and release the
         // lock if none of the clients need it.
         bool needsWakeLock = false;
@@ -939,8 +1038,9 @@
             (packageName == "") ? String8::format("unknown_package_pid_%d", pid) : packageName;
     String16 connOpPackageName =
             (opPackageName == String16("")) ? String16(connPackageName) : opPackageName;
+    bool hasSensorAccess = mUidPolicy->isUidActive(uid);
     sp<SensorEventConnection> result(new SensorEventConnection(this, uid, connPackageName,
-            requestedMode == DATA_INJECTION, connOpPackageName));
+            requestedMode == DATA_INJECTION, connOpPackageName, hasSensorAccess));
     if (requestedMode == DATA_INJECTION) {
         if (mActiveConnections.indexOf(result) < 0) {
             mActiveConnections.add(result);
@@ -1530,4 +1630,98 @@
     return false;
 }
 
+void SensorService::UidPolicy::registerSelf() {
+    ActivityManager am;
+    am.registerUidObserver(this, ActivityManager::UID_OBSERVER_GONE
+            | ActivityManager::UID_OBSERVER_IDLE
+            | ActivityManager::UID_OBSERVER_ACTIVE,
+            ActivityManager::PROCESS_STATE_UNKNOWN,
+            String16("android"));
+}
+
+void SensorService::UidPolicy::unregisterSelf() {
+    ActivityManager am;
+    am.unregisterUidObserver(this);
+}
+
+void SensorService::UidPolicy::onUidGone(__unused uid_t uid, __unused bool disabled) {
+    onUidIdle(uid, disabled);
+}
+
+void SensorService::UidPolicy::onUidActive(uid_t uid) {
+    {
+        Mutex::Autolock _l(mUidLock);
+        mActiveUids.insert(uid);
+    }
+    sp<SensorService> service = mService.promote();
+    if (service != nullptr) {
+        service->setSensorAccess(uid, true);
+    }
+}
+
+void SensorService::UidPolicy::onUidIdle(uid_t uid, __unused bool disabled) {
+    bool deleted = false;
+    {
+        Mutex::Autolock _l(mUidLock);
+        if (mActiveUids.erase(uid) > 0) {
+            deleted = true;
+        }
+    }
+    if (deleted) {
+        sp<SensorService> service = mService.promote();
+        if (service != nullptr) {
+            service->setSensorAccess(uid, false);
+        }
+    }
+}
+
+void SensorService::UidPolicy::addOverrideUid(uid_t uid, bool active) {
+    updateOverrideUid(uid, active, true);
+}
+
+void SensorService::UidPolicy::removeOverrideUid(uid_t uid) {
+    updateOverrideUid(uid, false, false);
+}
+
+void SensorService::UidPolicy::updateOverrideUid(uid_t uid, bool active, bool insert) {
+    bool wasActive = false;
+    bool isActive = false;
+    {
+        Mutex::Autolock _l(mUidLock);
+        wasActive = isUidActiveLocked(uid);
+        mOverrideUids.erase(uid);
+        if (insert) {
+            mOverrideUids.insert(std::pair<uid_t, bool>(uid, active));
+        }
+        isActive = isUidActiveLocked(uid);
+    }
+    if (wasActive != isActive) {
+        sp<SensorService> service = mService.promote();
+        if (service != nullptr) {
+            service->setSensorAccess(uid, isActive);
+        }
+    }
+}
+
+bool SensorService::UidPolicy::isUidActive(uid_t uid) {
+    // Non-app UIDs are considered always active
+    if (uid < FIRST_APPLICATION_UID) {
+        return true;
+    }
+    Mutex::Autolock _l(mUidLock);
+    return isUidActiveLocked(uid);
+}
+
+bool SensorService::UidPolicy::isUidActiveLocked(uid_t uid) {
+    // Non-app UIDs are considered always active
+    if (uid < FIRST_APPLICATION_UID) {
+        return true;
+    }
+    auto it = mOverrideUids.find(uid);
+    if (it != mOverrideUids.end()) {
+        return it->second;
+    }
+    return mActiveUids.find(uid) != mActiveUids.end();
+}
+
 }; // namespace android
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 3e18394..f71723d 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -21,6 +21,7 @@
 #include "RecentEventLogger.h"
 
 #include <binder/BinderService.h>
+#include <binder/IUidObserver.h>
 #include <cutils/compiler.h>
 #include <sensor/ISensorServer.h>
 #include <sensor/ISensorEventConnection.h>
@@ -85,6 +86,9 @@
     status_t flushSensor(const sp<SensorEventConnection>& connection,
                          const String16& opPackageName);
 
+
+    virtual status_t shellCommand(int in, int out, int err, Vector<String16>& args);
+
 private:
     friend class BinderService<SensorService>;
 
@@ -93,6 +97,40 @@
     class SensorEventAckReceiver;
     class SensorRegistrationInfo;
 
+    // If accessing a sensor we need to make sure the UID has access to it. If
+    // the app UID is idle then it cannot access sensors and gets no trigger
+    // events, no on-change events, flush event behavior does not change, and
+    // recurring events are the same as the first one delivered in idle state
+    // emulating no sensor change. As soon as the app UID transitions to an
+    // active state we will start reporting events as usual and vise versa. This
+    // approach transparently handles observing sensors while the app UID transitions
+    // between idle/active state avoiding to get stuck in a state receiving sensor
+    // data while idle or not receiving sensor data while active.
+    class UidPolicy : public BnUidObserver {
+        public:
+            explicit UidPolicy(wp<SensorService> service)
+                    : mService(service) {}
+            void registerSelf();
+            void unregisterSelf();
+
+            bool isUidActive(uid_t uid);
+
+            void onUidGone(uid_t uid, bool disabled);
+            void onUidActive(uid_t uid);
+            void onUidIdle(uid_t uid, bool disabled);
+
+            void addOverrideUid(uid_t uid, bool active);
+            void removeOverrideUid(uid_t uid);
+        private:
+            bool isUidActiveLocked(uid_t uid);
+            void updateOverrideUid(uid_t uid, bool active, bool insert);
+
+            Mutex mUidLock;
+            wp<SensorService> mService;
+            std::unordered_set<uid_t> mActiveUids;
+            std::unordered_map<uid_t, bool> mOverrideUids;
+    };
+
     enum Mode {
        // The regular operating mode where any application can register/unregister/call flush on
        // sensors.
@@ -161,7 +199,6 @@
     virtual int setOperationParameter(
             int32_t handle, int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints);
     virtual status_t dump(int fd, const Vector<String16>& args);
-
     String8 getSensorName(int handle) const;
     bool isVirtualSensor(int handle) const;
     sp<SensorInterface> getSensorInterfaceFromHandle(int handle) const;
@@ -225,6 +262,18 @@
     // Enable SCHED_FIFO priority for thread
     void enableSchedFifoMode();
 
+    // Sets whether the given UID can get sensor data
+    void setSensorAccess(uid_t uid, bool hasAccess);
+
+    // Overrides the UID state as if it is idle
+    status_t handleSetUidState(Vector<String16>& args, int err);
+    // Clears the override for the UID state
+    status_t handleResetUidState(Vector<String16>& args, int err);
+    // Gets the UID state
+    status_t handleGetUidState(Vector<String16>& args, int out, int err);
+    // Prints the shell command help
+    status_t printHelp(int out);
+
     static uint8_t sHmacGlobalKey[128];
     static bool sHmacGlobalKeyIsValid;
 
@@ -257,6 +306,8 @@
 
     int mNextSensorRegIndex;
     Vector<SensorRegistrationInfo> mLastNSensorRegistrations;
+
+    sp<UidPolicy> mUidPolicy;
 };
 
 } // namespace android