summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Manjeet Rulhania <mrulhania@google.com> 2023-01-20 15:50:43 -0800
committer Manjeet Rulhania <mrulhania@google.com> 2023-01-24 00:55:59 +0000
commitebdeed13ce3250c8848f5d81fd02a6c19dbbbf69 (patch)
tree286da6ec31f8afdf0fd45815699a24f69b3f2a80
parent0f5c1d95934a717e1ae00af5a419c60804db09ab (diff)
Add api to check if an app is in foreground or not.
This api is backed by existing appops logic and should only be used in the context of runtime permissions. This is different than ActivityManager's foreground check, which only consider proc state/importance. Fix: 265802019 API-Coverage-Bug: 266482297 Test: atest AppOpsUidStateTrackerTest Change-Id: I0a7ce12910199435c0c08f613798b2645455ab5e
-rw-r--r--services/api/current.txt8
-rw-r--r--services/core/java/com/android/server/appop/AppOpsManagerLocal.java36
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java10
-rw-r--r--services/core/java/com/android/server/appop/AppOpsUidStateTracker.java5
-rw-r--r--services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java80
-rw-r--r--services/core/java/com/android/server/appop/package-info.java22
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java30
7 files changed, 148 insertions, 43 deletions
diff --git a/services/api/current.txt b/services/api/current.txt
index e66bf4d76405..a92ccd42718e 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -46,6 +46,14 @@ package com.android.server.am {
}
+package com.android.server.appop {
+
+ public interface AppOpsManagerLocal {
+ method public boolean isUidInForeground(int);
+ }
+
+}
+
package com.android.server.pm {
public interface PackageManagerLocal {
diff --git a/services/core/java/com/android/server/appop/AppOpsManagerLocal.java b/services/core/java/com/android/server/appop/AppOpsManagerLocal.java
new file mode 100644
index 000000000000..a665896c3a8e
--- /dev/null
+++ b/services/core/java/com/android/server/appop/AppOpsManagerLocal.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appop;
+
+import android.annotation.SystemApi;
+
+/**
+ * In-process app ops API for mainline modules.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public interface AppOpsManagerLocal {
+
+ /**
+ * Determines if the UID is in foreground in the same way as how foreground runtime
+ * permissions work.
+ *
+ * @return Returns {@code true} if the given UID is in the foreground.
+ */
+ boolean isUidInForeground(int uid);
+}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 9c6cae355225..c50f2b789f98 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -978,6 +978,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
public void publish() {
ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
LocalServices.addService(AppOpsManagerInternal.class, mAppOpsManagerInternal);
+ LocalServices.addService(AppOpsManagerLocal.class, new AppOpsManagerLocalImpl());
}
/** Handler for work when packages are removed or updated */
@@ -6138,6 +6139,15 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
}
}
+ private final class AppOpsManagerLocalImpl implements AppOpsManagerLocal {
+ @Override
+ public boolean isUidInForeground(int uid) {
+ synchronized (AppOpsService.this) {
+ return mUidStateTracker.isUidInForeground(uid);
+ }
+ }
+ }
+
private final class AppOpsManagerInternalImpl extends AppOpsManagerInternal {
@Override public void setDeviceAndProfileOwners(SparseIntArray owners) {
synchronized (AppOpsService.this) {
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java b/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java
index 742bf4b6ebc7..18ea8cfc1386 100644
--- a/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java
@@ -89,6 +89,11 @@ interface AppOpsUidStateTracker {
int getUidState(int uid);
/**
+ * Determines if the uid is in foreground.
+ */
+ boolean isUidInForeground(int uid);
+
+ /**
* Given a uid, code, and mode, resolve any foregroundness to MODE_IGNORED or MODE_ALLOWED
*/
int evalMode(int uid, int code, int mode);
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
index 5114bd59f084..49279d44ea7c 100644
--- a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
@@ -24,8 +24,10 @@ import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManager.ProcessCapability;
import static android.app.AppOpsManager.MIN_PRIORITY_UID_STATE;
import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_FOREGROUND;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.OP_CAMERA;
+import static android.app.AppOpsManager.OP_NONE;
import static android.app.AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
@@ -124,67 +126,61 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
@Override
public int evalMode(int uid, int code, int mode) {
- if (mode != AppOpsManager.MODE_FOREGROUND) {
+ if (mode != MODE_FOREGROUND) {
return mode;
}
- int uidStateValue;
- int capability;
- boolean visibleAppWidget;
- boolean pendingTop;
- boolean tempAllowlist;
- uidStateValue = getUidState(uid);
- capability = getUidCapability(uid);
- visibleAppWidget = getUidVisibleAppWidget(uid);
- pendingTop = mActivityManagerInternal.isPendingTopUid(uid);
- tempAllowlist = mActivityManagerInternal.isTempAllowlistedForFgsWhileInUse(uid);
-
- int result = evalMode(uidStateValue, code, mode, capability, visibleAppWidget, pendingTop,
- tempAllowlist);
- mEventLog.logEvalForegroundMode(uid, uidStateValue, capability, code, result);
+ int uidState = getUidState(uid);
+ int uidCapability = getUidCapability(uid);
+ int result = evalModeInternal(uid, code, uidState, uidCapability);
+
+ mEventLog.logEvalForegroundMode(uid, uidState, uidCapability, code, result);
return result;
}
- private static int evalMode(int uidState, int code, int mode, int capability,
- boolean appWidgetVisible, boolean pendingTop, boolean tempAllowlist) {
- if (mode != AppOpsManager.MODE_FOREGROUND) {
- return mode;
- }
+ private int evalModeInternal(int uid, int code, int uidState, int uidCapability) {
- if (appWidgetVisible || pendingTop || tempAllowlist) {
+ if (getUidVisibleAppWidget(uid) || mActivityManagerInternal.isPendingTopUid(uid)
+ || mActivityManagerInternal.isTempAllowlistedForFgsWhileInUse(uid)) {
return MODE_ALLOWED;
}
- switch (code) {
+ int opCapability = getOpCapability(code);
+ if (opCapability != PROCESS_CAPABILITY_NONE) {
+ if ((uidCapability & opCapability) == 0) {
+ return MODE_IGNORED;
+ } else {
+ return MODE_ALLOWED;
+ }
+ }
+
+ if (uidState > AppOpsManager.resolveFirstUnrestrictedUidState(code)) {
+ return MODE_IGNORED;
+ }
+
+ return MODE_ALLOWED;
+ }
+
+ private int getOpCapability(int opCode) {
+ switch (opCode) {
case AppOpsManager.OP_FINE_LOCATION:
case AppOpsManager.OP_COARSE_LOCATION:
case AppOpsManager.OP_MONITOR_LOCATION:
case AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION:
- if ((capability & PROCESS_CAPABILITY_FOREGROUND_LOCATION) == 0) {
- return MODE_IGNORED;
- } else {
- return MODE_ALLOWED;
- }
+ return PROCESS_CAPABILITY_FOREGROUND_LOCATION;
case OP_CAMERA:
- if ((capability & PROCESS_CAPABILITY_FOREGROUND_CAMERA) == 0) {
- return MODE_IGNORED;
- } else {
- return MODE_ALLOWED;
- }
+ return PROCESS_CAPABILITY_FOREGROUND_CAMERA;
case OP_RECORD_AUDIO:
case OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO:
- if ((capability & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) == 0) {
- return MODE_IGNORED;
- } else {
- return MODE_ALLOWED;
- }
- }
-
- if (uidState > AppOpsManager.resolveFirstUnrestrictedUidState(code)) {
- return MODE_IGNORED;
+ return PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
+ default:
+ return PROCESS_CAPABILITY_NONE;
}
+ }
- return MODE_ALLOWED;
+ @Override
+ public boolean isUidInForeground(int uid) {
+ return evalMode(uid, OP_NONE, MODE_FOREGROUND) == MODE_ALLOWED;
}
@Override
diff --git a/services/core/java/com/android/server/appop/package-info.java b/services/core/java/com/android/server/appop/package-info.java
new file mode 100644
index 000000000000..3b8fb9dfa07b
--- /dev/null
+++ b/services/core/java/com/android/server/appop/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @hide
+ * TODO(b/146466118) remove this javadoc tag
+ */
+@android.annotation.Hide
+package com.android.server.appop;
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
index 98e895a86f9e..cd5ac7bcb1e5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
@@ -35,6 +35,8 @@ import static android.app.AppOpsManager.UID_STATE_TOP;
import static com.android.server.appop.AppOpsUidStateTracker.processStateToUidState;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -754,6 +756,32 @@ public class AppOpsUidStateTrackerTest {
verify(cb, never()).onUidStateChanged(anyInt(), anyInt(), anyBoolean());
}
+ @Test
+ public void testIsUidInForegroundForBackgroundState() {
+ procStateBuilder(UID)
+ .backgroundState()
+ .update();
+ assertFalse(mIntf.isUidInForeground(UID));
+
+ procStateBuilder(UID)
+ .nonExistentState()
+ .update();
+ assertFalse(mIntf.isUidInForeground(UID));
+ }
+
+ @Test
+ public void testIsUidInForegroundForForegroundState() {
+ procStateBuilder(UID)
+ .topState()
+ .update();
+ assertTrue(mIntf.isUidInForeground(UID));
+
+ procStateBuilder(UID)
+ .foregroundServiceState()
+ .update();
+ assertTrue(mIntf.isUidInForeground(UID));
+ }
+
public void testUidStateChangedCallback(int initialState, int finalState) {
int initialUidState = processStateToUidState(initialState);
int finalUidState = processStateToUidState(finalState);
@@ -806,7 +834,7 @@ public class AppOpsUidStateTrackerTest {
private AppOpsUidStateTracker mIntf;
private int mUid;
private int mProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
- private int mCapability = 0;
+ private int mCapability = ActivityManager.PROCESS_CAPABILITY_NONE;
private UidProcStateUpdateBuilder(AppOpsUidStateTracker intf, int uid) {
mUid = uid;