diff --git a/services/api/current.txt b/services/api/current.txt index e66bf4d764057..a92ccd42718ed 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 0000000000000..a665896c3a8ed --- /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 9c6cae3552251..c50f2b789f98a 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 742bf4b6ebc7f..18ea8cfc13866 100644 --- a/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java +++ b/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java @@ -88,6 +88,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 */ diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java index 5114bd59f0842..49279d44ea7cb 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,60 +126,32 @@ 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 uidState = getUidState(uid); + int uidCapability = getUidCapability(uid); + int result = evalModeInternal(uid, code, uidState, uidCapability); - int result = evalMode(uidStateValue, code, mode, capability, visibleAppWidget, pendingTop, - tempAllowlist); - mEventLog.logEvalForegroundMode(uid, uidStateValue, capability, code, result); + 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) { - 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; - } - case OP_CAMERA: - if ((capability & PROCESS_CAPABILITY_FOREGROUND_CAMERA) == 0) { - return MODE_IGNORED; - } else { - return MODE_ALLOWED; - } - 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; - } + 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)) { @@ -187,6 +161,28 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker { 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: + return PROCESS_CAPABILITY_FOREGROUND_LOCATION; + case OP_CAMERA: + return PROCESS_CAPABILITY_FOREGROUND_CAMERA; + case OP_RECORD_AUDIO: + case OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO: + return PROCESS_CAPABILITY_FOREGROUND_MICROPHONE; + default: + return PROCESS_CAPABILITY_NONE; + } + } + + @Override + public boolean isUidInForeground(int uid) { + return evalMode(uid, OP_NONE, MODE_FOREGROUND) == MODE_ALLOWED; + } + @Override public void addUidStateChangedCallback(Executor executor, UidStateChangedCallback callback) { if (mUidStateChangedCallbacks.containsKey(callback)) { 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 0000000000000..3b8fb9dfa07b7 --- /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 98e895a86f9e5..cd5ac7bcb1e53 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;