Merge "Add api to check if an app is in foreground or not."

This commit is contained in:
Manjeet Rulhania
2023-01-24 18:36:55 +00:00
committed by Android (Google) Code Review
7 changed files with 149 additions and 44 deletions

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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
*/

View File

@@ -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)) {

View File

@@ -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;

View File

@@ -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;