Merge "Add logic to show apps running certain jobs in Task Manager." into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
816123320d
@@ -16,7 +16,10 @@
|
|||||||
|
|
||||||
package android.app;
|
package android.app;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.RequiresPermission;
|
||||||
import android.app.job.IJobScheduler;
|
import android.app.job.IJobScheduler;
|
||||||
|
import android.app.job.IUserVisibleJobObserver;
|
||||||
import android.app.job.JobInfo;
|
import android.app.job.JobInfo;
|
||||||
import android.app.job.JobScheduler;
|
import android.app.job.JobScheduler;
|
||||||
import android.app.job.JobSnapshot;
|
import android.app.job.JobSnapshot;
|
||||||
@@ -119,4 +122,28 @@ public class JobSchedulerImpl extends JobScheduler {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresPermission(allOf = {
|
||||||
|
android.Manifest.permission.MANAGE_ACTIVITY_TASKS,
|
||||||
|
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})
|
||||||
|
@Override
|
||||||
|
public void registerUserVisibleJobObserver(@NonNull IUserVisibleJobObserver observer) {
|
||||||
|
// TODO(255767350): implement
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresPermission(allOf = {
|
||||||
|
android.Manifest.permission.MANAGE_ACTIVITY_TASKS,
|
||||||
|
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})
|
||||||
|
@Override
|
||||||
|
public void unregisterUserVisibleJobObserver(@NonNull IUserVisibleJobObserver observer) {
|
||||||
|
// TODO(255767350): implement
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresPermission(allOf = {
|
||||||
|
android.Manifest.permission.MANAGE_ACTIVITY_TASKS,
|
||||||
|
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})
|
||||||
|
@Override
|
||||||
|
public void stopUserVisibleJobsForUser(@NonNull String packageName, int userId) {
|
||||||
|
// TODO(255767350): implement
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 android.app.job;
|
||||||
|
|
||||||
|
import android.app.job.UserVisibleJobSummary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IPC protocol to know about user-visible job activity.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
oneway interface IUserVisibleJobObserver {
|
||||||
|
/**
|
||||||
|
* Notify the client of all changes to a user-visible jobs' state.
|
||||||
|
* @param summary A token/summary that uniquely identifies and details a single running job
|
||||||
|
* @param isRunning whether the job is currently running or not
|
||||||
|
*/
|
||||||
|
void onUserVisibleJobStateChanged(in UserVisibleJobSummary summary, boolean isRunning);
|
||||||
|
}
|
||||||
@@ -237,4 +237,32 @@ public abstract class JobScheduler {
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("HiddenAbstractMethod")
|
@SuppressWarnings("HiddenAbstractMethod")
|
||||||
public abstract List<JobSnapshot> getAllJobSnapshots();
|
public abstract List<JobSnapshot> getAllJobSnapshots();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@RequiresPermission(allOf = {
|
||||||
|
android.Manifest.permission.MANAGE_ACTIVITY_TASKS,
|
||||||
|
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})
|
||||||
|
@SuppressWarnings("HiddenAbstractMethod")
|
||||||
|
public abstract void registerUserVisibleJobObserver(@NonNull IUserVisibleJobObserver observer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@RequiresPermission(allOf = {
|
||||||
|
android.Manifest.permission.MANAGE_ACTIVITY_TASKS,
|
||||||
|
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})
|
||||||
|
@SuppressWarnings("HiddenAbstractMethod")
|
||||||
|
public abstract void unregisterUserVisibleJobObserver(
|
||||||
|
@NonNull IUserVisibleJobObserver observer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
@RequiresPermission(allOf = {
|
||||||
|
android.Manifest.permission.MANAGE_ACTIVITY_TASKS,
|
||||||
|
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})
|
||||||
|
@SuppressWarnings("HiddenAbstractMethod")
|
||||||
|
public abstract void stopUserVisibleJobsForUser(@NonNull String packageName, int userId);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2022 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 android.app.job;
|
||||||
|
|
||||||
|
parcelable UserVisibleJobSummary;
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 android.app.job;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Summary of a scheduled job that the user is meant to be aware of.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public class UserVisibleJobSummary implements Parcelable {
|
||||||
|
private final int mCallingUid;
|
||||||
|
private final int mSourceUserId;
|
||||||
|
@NonNull
|
||||||
|
private final String mSourcePackageName;
|
||||||
|
private final int mJobId;
|
||||||
|
|
||||||
|
public UserVisibleJobSummary(int callingUid, int sourceUserId,
|
||||||
|
@NonNull String sourcePackageName, int jobId) {
|
||||||
|
mCallingUid = callingUid;
|
||||||
|
mSourceUserId = sourceUserId;
|
||||||
|
mSourcePackageName = sourcePackageName;
|
||||||
|
mJobId = jobId;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected UserVisibleJobSummary(Parcel in) {
|
||||||
|
mCallingUid = in.readInt();
|
||||||
|
mSourceUserId = in.readInt();
|
||||||
|
mSourcePackageName = in.readString();
|
||||||
|
mJobId = in.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCallingUid() {
|
||||||
|
return mCallingUid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getJobId() {
|
||||||
|
return mJobId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSourceUserId() {
|
||||||
|
return mSourceUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSourcePackageName() {
|
||||||
|
return mSourcePackageName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof UserVisibleJobSummary)) return false;
|
||||||
|
UserVisibleJobSummary that = (UserVisibleJobSummary) o;
|
||||||
|
return mCallingUid == that.mCallingUid
|
||||||
|
&& mSourceUserId == that.mSourceUserId
|
||||||
|
&& mSourcePackageName.equals(that.mSourcePackageName)
|
||||||
|
&& mJobId == that.mJobId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = 0;
|
||||||
|
result = 31 * result + mCallingUid;
|
||||||
|
result = 31 * result + mSourceUserId;
|
||||||
|
result = 31 * result + mSourcePackageName.hashCode();
|
||||||
|
result = 31 * result + mJobId;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "UserVisibleJobSummary{"
|
||||||
|
+ "callingUid=" + mCallingUid
|
||||||
|
+ ", sourceUserId=" + mSourceUserId
|
||||||
|
+ ", sourcePackageName='" + mSourcePackageName + "'"
|
||||||
|
+ ", jobId=" + mJobId
|
||||||
|
+ "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeInt(mCallingUid);
|
||||||
|
dest.writeInt(mSourceUserId);
|
||||||
|
dest.writeString(mSourcePackageName);
|
||||||
|
dest.writeInt(mJobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Creator<UserVisibleJobSummary> CREATOR =
|
||||||
|
new Creator<UserVisibleJobSummary>() {
|
||||||
|
@Override
|
||||||
|
public UserVisibleJobSummary createFromParcel(Parcel in) {
|
||||||
|
return new UserVisibleJobSummary(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserVisibleJobSummary[] newArray(int size) {
|
||||||
|
return new UserVisibleJobSummary[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -555,6 +555,12 @@ public final class SystemUiDeviceConfigFlags {
|
|||||||
public static final String TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS =
|
public static final String TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS =
|
||||||
"show_stop_button_for_user_allowlisted_apps";
|
"show_stop_button_for_user_allowlisted_apps";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (boolean) Whether the task manager should show apps running user-visible jobs.
|
||||||
|
*/
|
||||||
|
public static final String TASK_MANAGER_SHOW_USER_VISIBLE_JOBS =
|
||||||
|
"task_manager_show_user_visible_jobs";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* (boolean) Whether to show notification volume control slider separate from ring.
|
* (boolean) Whether to show notification volume control slider separate from ring.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import android.app.StatsManager;
|
|||||||
import android.app.UiModeManager;
|
import android.app.UiModeManager;
|
||||||
import android.app.WallpaperManager;
|
import android.app.WallpaperManager;
|
||||||
import android.app.admin.DevicePolicyManager;
|
import android.app.admin.DevicePolicyManager;
|
||||||
|
import android.app.job.JobScheduler;
|
||||||
import android.app.role.RoleManager;
|
import android.app.role.RoleManager;
|
||||||
import android.app.smartspace.SmartspaceManager;
|
import android.app.smartspace.SmartspaceManager;
|
||||||
import android.app.trust.TrustManager;
|
import android.app.trust.TrustManager;
|
||||||
@@ -277,6 +278,12 @@ public class FrameworkServicesModule {
|
|||||||
context.getSystemService(BiometricManager.class);
|
context.getSystemService(BiometricManager.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
static JobScheduler provideJobScheduler(Context context) {
|
||||||
|
return context.getSystemService(JobScheduler.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
static InteractionJankMonitor provideInteractionJankMonitor() {
|
static InteractionJankMonitor provideInteractionJankMonitor() {
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ package com.android.systemui.qs
|
|||||||
|
|
||||||
import android.app.IActivityManager
|
import android.app.IActivityManager
|
||||||
import android.app.IForegroundServiceObserver
|
import android.app.IForegroundServiceObserver
|
||||||
|
import android.app.job.IUserVisibleJobObserver
|
||||||
|
import android.app.job.JobScheduler
|
||||||
|
import android.app.job.UserVisibleJobSummary
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@@ -47,6 +50,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||||||
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED
|
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED
|
||||||
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_FOOTER_DOT
|
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_FOOTER_DOT
|
||||||
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS
|
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS
|
||||||
|
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS
|
||||||
import com.android.internal.jank.InteractionJankMonitor
|
import com.android.internal.jank.InteractionJankMonitor
|
||||||
import com.android.systemui.Dumpable
|
import com.android.systemui.Dumpable
|
||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
@@ -92,6 +96,8 @@ interface FgsManagerController {
|
|||||||
*/
|
*/
|
||||||
val showFooterDot: StateFlow<Boolean>
|
val showFooterDot: StateFlow<Boolean>
|
||||||
|
|
||||||
|
val includesUserVisibleJobs: Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize this controller. This should be called once, before this controller is used for
|
* Initialize this controller. This should be called once, before this controller is used for
|
||||||
* the first time.
|
* the first time.
|
||||||
@@ -141,19 +147,21 @@ class FgsManagerControllerImpl @Inject constructor(
|
|||||||
@Background private val backgroundExecutor: Executor,
|
@Background private val backgroundExecutor: Executor,
|
||||||
private val systemClock: SystemClock,
|
private val systemClock: SystemClock,
|
||||||
private val activityManager: IActivityManager,
|
private val activityManager: IActivityManager,
|
||||||
|
private val jobScheduler: JobScheduler,
|
||||||
private val packageManager: PackageManager,
|
private val packageManager: PackageManager,
|
||||||
private val userTracker: UserTracker,
|
private val userTracker: UserTracker,
|
||||||
private val deviceConfigProxy: DeviceConfigProxy,
|
private val deviceConfigProxy: DeviceConfigProxy,
|
||||||
private val dialogLaunchAnimator: DialogLaunchAnimator,
|
private val dialogLaunchAnimator: DialogLaunchAnimator,
|
||||||
private val broadcastDispatcher: BroadcastDispatcher,
|
private val broadcastDispatcher: BroadcastDispatcher,
|
||||||
private val dumpManager: DumpManager
|
private val dumpManager: DumpManager
|
||||||
) : IForegroundServiceObserver.Stub(), Dumpable, FgsManagerController {
|
) : Dumpable, FgsManagerController {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val INTERACTION_JANK_TAG = "active_background_apps"
|
private const val INTERACTION_JANK_TAG = "active_background_apps"
|
||||||
private const val DEFAULT_TASK_MANAGER_ENABLED = true
|
private const val DEFAULT_TASK_MANAGER_ENABLED = true
|
||||||
private const val DEFAULT_TASK_MANAGER_SHOW_FOOTER_DOT = false
|
private const val DEFAULT_TASK_MANAGER_SHOW_FOOTER_DOT = false
|
||||||
private const val DEFAULT_TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS = true
|
private const val DEFAULT_TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS = true
|
||||||
|
private const val DEFAULT_TASK_MANAGER_SHOW_USER_VISIBLE_JOBS = false
|
||||||
}
|
}
|
||||||
|
|
||||||
override var newChangesSinceDialogWasDismissed = false
|
override var newChangesSinceDialogWasDismissed = false
|
||||||
@@ -167,6 +175,11 @@ class FgsManagerControllerImpl @Inject constructor(
|
|||||||
|
|
||||||
private var showStopBtnForUserAllowlistedApps = false
|
private var showStopBtnForUserAllowlistedApps = false
|
||||||
|
|
||||||
|
private var showUserVisibleJobs = DEFAULT_TASK_MANAGER_SHOW_USER_VISIBLE_JOBS
|
||||||
|
|
||||||
|
override val includesUserVisibleJobs: Boolean
|
||||||
|
get() = showUserVisibleJobs
|
||||||
|
|
||||||
override val numRunningPackages: Int
|
override val numRunningPackages: Int
|
||||||
get() {
|
get() {
|
||||||
synchronized(lock) {
|
synchronized(lock) {
|
||||||
@@ -186,7 +199,7 @@ class FgsManagerControllerImpl @Inject constructor(
|
|||||||
private var currentProfileIds = mutableSetOf<Int>()
|
private var currentProfileIds = mutableSetOf<Int>()
|
||||||
|
|
||||||
@GuardedBy("lock")
|
@GuardedBy("lock")
|
||||||
private val runningServiceTokens = mutableMapOf<UserPackage, StartTimeAndTokens>()
|
private val runningTaskIdentifiers = mutableMapOf<UserPackage, StartTimeAndIdentifiers>()
|
||||||
|
|
||||||
@GuardedBy("lock")
|
@GuardedBy("lock")
|
||||||
private var dialog: SystemUIDialog? = null
|
private var dialog: SystemUIDialog? = null
|
||||||
@@ -210,13 +223,29 @@ class FgsManagerControllerImpl @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val foregroundServiceObserver = ForegroundServiceObserver()
|
||||||
|
|
||||||
|
private val userVisibleJobObserver = UserVisibleJobObserver()
|
||||||
|
|
||||||
override fun init() {
|
override fun init() {
|
||||||
synchronized(lock) {
|
synchronized(lock) {
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showUserVisibleJobs = deviceConfigProxy.getBoolean(
|
||||||
|
NAMESPACE_SYSTEMUI,
|
||||||
|
TASK_MANAGER_SHOW_USER_VISIBLE_JOBS, DEFAULT_TASK_MANAGER_SHOW_USER_VISIBLE_JOBS)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
activityManager.registerForegroundServiceObserver(this)
|
activityManager.registerForegroundServiceObserver(foregroundServiceObserver)
|
||||||
|
// Clumping FGS and user-visible jobs here and showing a single entry and button
|
||||||
|
// for them is the easiest way to get user-visible jobs showing in Task Manager.
|
||||||
|
// Ideally, we would have dedicated UI in task manager for the user-visible jobs.
|
||||||
|
// TODO(255768978): distinguish jobs from FGS and give users more control
|
||||||
|
if (showUserVisibleJobs) {
|
||||||
|
jobScheduler.registerUserVisibleJobObserver(userVisibleJobObserver)
|
||||||
|
}
|
||||||
} catch (e: RemoteException) {
|
} catch (e: RemoteException) {
|
||||||
e.rethrowFromSystemServer()
|
e.rethrowFromSystemServer()
|
||||||
}
|
}
|
||||||
@@ -235,6 +264,12 @@ class FgsManagerControllerImpl @Inject constructor(
|
|||||||
showStopBtnForUserAllowlistedApps = it.getBoolean(
|
showStopBtnForUserAllowlistedApps = it.getBoolean(
|
||||||
TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS,
|
TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS,
|
||||||
showStopBtnForUserAllowlistedApps)
|
showStopBtnForUserAllowlistedApps)
|
||||||
|
var wasShowingUserVisibleJobs = showUserVisibleJobs
|
||||||
|
showUserVisibleJobs = it.getBoolean(
|
||||||
|
TASK_MANAGER_SHOW_USER_VISIBLE_JOBS, showUserVisibleJobs)
|
||||||
|
if (showUserVisibleJobs != wasShowingUserVisibleJobs) {
|
||||||
|
onShowUserVisibleJobsFlagChanged()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_isAvailable.value = deviceConfigProxy.getBoolean(
|
_isAvailable.value = deviceConfigProxy.getBoolean(
|
||||||
@@ -269,32 +304,6 @@ class FgsManagerControllerImpl @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onForegroundStateChanged(
|
|
||||||
token: IBinder,
|
|
||||||
packageName: String,
|
|
||||||
userId: Int,
|
|
||||||
isForeground: Boolean
|
|
||||||
) {
|
|
||||||
synchronized(lock) {
|
|
||||||
val userPackageKey = UserPackage(userId, packageName)
|
|
||||||
if (isForeground) {
|
|
||||||
runningServiceTokens.getOrPut(userPackageKey) { StartTimeAndTokens(systemClock) }
|
|
||||||
.addToken(token)
|
|
||||||
} else {
|
|
||||||
if (runningServiceTokens[userPackageKey]?.also {
|
|
||||||
it.removeToken(token)
|
|
||||||
}?.isEmpty() == true
|
|
||||||
) {
|
|
||||||
runningServiceTokens.remove(userPackageKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateNumberOfVisibleRunningPackagesLocked()
|
|
||||||
|
|
||||||
updateAppItemsLocked()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
@GuardedBy("lock")
|
||||||
private val onNumberOfPackagesChangedListeners =
|
private val onNumberOfPackagesChangedListeners =
|
||||||
mutableSetOf<FgsManagerController.OnNumberOfPackagesChangedListener>()
|
mutableSetOf<FgsManagerController.OnNumberOfPackagesChangedListener>()
|
||||||
@@ -336,7 +345,7 @@ class FgsManagerControllerImpl @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getNumVisiblePackagesLocked(): Int {
|
private fun getNumVisiblePackagesLocked(): Int {
|
||||||
return runningServiceTokens.keys.count {
|
return runningTaskIdentifiers.keys.count {
|
||||||
it.uiControl != UIControl.HIDE_ENTRY && currentProfileIds.contains(it.userId)
|
it.uiControl != UIControl.HIDE_ENTRY && currentProfileIds.contains(it.userId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -361,7 +370,7 @@ class FgsManagerControllerImpl @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getNumVisibleButtonsLocked(): Int {
|
private fun getNumVisibleButtonsLocked(): Int {
|
||||||
return runningServiceTokens.keys.count {
|
return runningTaskIdentifiers.keys.count {
|
||||||
it.uiControl != UIControl.HIDE_BUTTON && currentProfileIds.contains(it.userId)
|
it.uiControl != UIControl.HIDE_BUTTON && currentProfileIds.contains(it.userId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -372,7 +381,7 @@ class FgsManagerControllerImpl @Inject constructor(
|
|||||||
synchronized(lock) {
|
synchronized(lock) {
|
||||||
if (dialog == null) {
|
if (dialog == null) {
|
||||||
|
|
||||||
runningServiceTokens.keys.forEach {
|
runningTaskIdentifiers.keys.forEach {
|
||||||
it.updateUiControl()
|
it.updateUiControl()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -434,17 +443,17 @@ class FgsManagerControllerImpl @Inject constructor(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val addedPackages = runningServiceTokens.keys.filter {
|
val addedPackages = runningTaskIdentifiers.keys.filter {
|
||||||
currentProfileIds.contains(it.userId) &&
|
currentProfileIds.contains(it.userId) &&
|
||||||
it.uiControl != UIControl.HIDE_ENTRY && runningApps[it]?.stopped != true
|
it.uiControl != UIControl.HIDE_ENTRY && runningApps[it]?.stopped != true
|
||||||
}
|
}
|
||||||
val removedPackages = runningApps.keys.filter { !runningServiceTokens.containsKey(it) }
|
val removedPackages = runningApps.keys.filter { !runningTaskIdentifiers.containsKey(it) }
|
||||||
|
|
||||||
addedPackages.forEach {
|
addedPackages.forEach {
|
||||||
val ai = packageManager.getApplicationInfoAsUser(it.packageName, 0, it.userId)
|
val ai = packageManager.getApplicationInfoAsUser(it.packageName, 0, it.userId)
|
||||||
runningApps[it] = RunningApp(
|
runningApps[it] = RunningApp(
|
||||||
it.userId, it.packageName,
|
it.userId, it.packageName,
|
||||||
runningServiceTokens[it]!!.startTime, it.uiControl,
|
runningTaskIdentifiers[it]!!.startTime, it.uiControl,
|
||||||
packageManager.getApplicationLabel(ai),
|
packageManager.getApplicationLabel(ai),
|
||||||
packageManager.getUserBadgedIcon(
|
packageManager.getUserBadgedIcon(
|
||||||
packageManager.getApplicationIcon(ai), UserHandle.of(it.userId)
|
packageManager.getApplicationIcon(ai), UserHandle.of(it.userId)
|
||||||
@@ -471,7 +480,41 @@ class FgsManagerControllerImpl @Inject constructor(
|
|||||||
|
|
||||||
private fun stopPackage(userId: Int, packageName: String, timeStarted: Long) {
|
private fun stopPackage(userId: Int, packageName: String, timeStarted: Long) {
|
||||||
logEvent(stopped = true, packageName, userId, timeStarted)
|
logEvent(stopped = true, packageName, userId, timeStarted)
|
||||||
activityManager.stopAppForUser(packageName, userId)
|
val userPackageKey = UserPackage(userId, packageName)
|
||||||
|
if (showUserVisibleJobs &&
|
||||||
|
runningTaskIdentifiers[userPackageKey]?.hasRunningJobs() == true) {
|
||||||
|
// TODO(255768978): allow fine-grained job control
|
||||||
|
jobScheduler.stopUserVisibleJobsForUser(packageName, userId)
|
||||||
|
}
|
||||||
|
if (runningTaskIdentifiers[userPackageKey]?.hasFgs() == true) {
|
||||||
|
activityManager.stopAppForUser(packageName, userId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onShowUserVisibleJobsFlagChanged() {
|
||||||
|
if (showUserVisibleJobs) {
|
||||||
|
jobScheduler.registerUserVisibleJobObserver(userVisibleJobObserver)
|
||||||
|
} else {
|
||||||
|
jobScheduler.unregisterUserVisibleJobObserver(userVisibleJobObserver)
|
||||||
|
|
||||||
|
synchronized(lock) {
|
||||||
|
for ((userPackage, startTimeAndIdentifiers) in runningTaskIdentifiers) {
|
||||||
|
if (startTimeAndIdentifiers.hasFgs()) {
|
||||||
|
// The app still has FGS running, so all we need to do is remove
|
||||||
|
// the job summaries
|
||||||
|
startTimeAndIdentifiers.clearJobSummaries()
|
||||||
|
} else {
|
||||||
|
// The app only has user-visible jobs running, so remove it from
|
||||||
|
// the map altogether
|
||||||
|
runningTaskIdentifiers.remove(userPackage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateNumberOfVisibleRunningPackagesLocked()
|
||||||
|
|
||||||
|
updateAppItemsLocked()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun logEvent(stopped: Boolean, packageName: String, userId: Int, timeStarted: Long) {
|
private fun logEvent(stopped: Boolean, packageName: String, userId: Int, timeStarted: Long) {
|
||||||
@@ -564,6 +607,62 @@ class FgsManagerControllerImpl @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inner class ForegroundServiceObserver : IForegroundServiceObserver.Stub() {
|
||||||
|
override fun onForegroundStateChanged(
|
||||||
|
token: IBinder,
|
||||||
|
packageName: String,
|
||||||
|
userId: Int,
|
||||||
|
isForeground: Boolean
|
||||||
|
) {
|
||||||
|
synchronized(lock) {
|
||||||
|
val userPackageKey = UserPackage(userId, packageName)
|
||||||
|
if (isForeground) {
|
||||||
|
runningTaskIdentifiers
|
||||||
|
.getOrPut(userPackageKey) { StartTimeAndIdentifiers(systemClock) }
|
||||||
|
.addFgsToken(token)
|
||||||
|
} else {
|
||||||
|
if (runningTaskIdentifiers[userPackageKey]?.also {
|
||||||
|
it.removeFgsToken(token)
|
||||||
|
}?.isEmpty() == true
|
||||||
|
) {
|
||||||
|
runningTaskIdentifiers.remove(userPackageKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateNumberOfVisibleRunningPackagesLocked()
|
||||||
|
|
||||||
|
updateAppItemsLocked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class UserVisibleJobObserver : IUserVisibleJobObserver.Stub() {
|
||||||
|
override fun onUserVisibleJobStateChanged(
|
||||||
|
summary: UserVisibleJobSummary,
|
||||||
|
isRunning: Boolean
|
||||||
|
) {
|
||||||
|
synchronized(lock) {
|
||||||
|
val userPackageKey = UserPackage(summary.sourceUserId, summary.sourcePackageName)
|
||||||
|
if (isRunning) {
|
||||||
|
runningTaskIdentifiers
|
||||||
|
.getOrPut(userPackageKey) { StartTimeAndIdentifiers(systemClock) }
|
||||||
|
.addJobSummary(summary)
|
||||||
|
} else {
|
||||||
|
if (runningTaskIdentifiers[userPackageKey]?.also {
|
||||||
|
it.removeJobSummary(summary)
|
||||||
|
}?.isEmpty() == true
|
||||||
|
) {
|
||||||
|
runningTaskIdentifiers.remove(userPackageKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateNumberOfVisibleRunningPackagesLocked()
|
||||||
|
|
||||||
|
updateAppItemsLocked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private inner class UserPackage(
|
private inner class UserPackage(
|
||||||
val userId: Int,
|
val userId: Int,
|
||||||
val packageName: String
|
val packageName: String
|
||||||
@@ -630,37 +729,64 @@ class FgsManagerControllerImpl @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class StartTimeAndTokens(
|
private data class StartTimeAndIdentifiers(
|
||||||
val systemClock: SystemClock
|
val systemClock: SystemClock
|
||||||
) {
|
) {
|
||||||
val startTime = systemClock.elapsedRealtime()
|
val startTime = systemClock.elapsedRealtime()
|
||||||
val tokens = mutableSetOf<IBinder>()
|
val fgsTokens = mutableSetOf<IBinder>()
|
||||||
|
val jobSummaries = mutableSetOf<UserVisibleJobSummary>()
|
||||||
|
|
||||||
fun addToken(token: IBinder) {
|
fun addJobSummary(summary: UserVisibleJobSummary) {
|
||||||
tokens.add(token)
|
jobSummaries.add(summary)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeToken(token: IBinder) {
|
fun clearJobSummaries() {
|
||||||
tokens.remove(token)
|
jobSummaries.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeJobSummary(summary: UserVisibleJobSummary) {
|
||||||
|
jobSummaries.remove(summary)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addFgsToken(token: IBinder) {
|
||||||
|
fgsTokens.add(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeFgsToken(token: IBinder) {
|
||||||
|
fgsTokens.remove(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasFgs(): Boolean {
|
||||||
|
return !fgsTokens.isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hasRunningJobs(): Boolean {
|
||||||
|
return !jobSummaries.isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isEmpty(): Boolean {
|
fun isEmpty(): Boolean {
|
||||||
return tokens.isEmpty()
|
return fgsTokens.isEmpty() && jobSummaries.isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun dump(pw: PrintWriter) {
|
fun dump(pw: PrintWriter) {
|
||||||
pw.println("StartTimeAndTokens: [")
|
pw.println("StartTimeAndIdentifiers: [")
|
||||||
pw.indentIfPossible {
|
pw.indentIfPossible {
|
||||||
pw.println(
|
pw.println(
|
||||||
"startTime=$startTime (time running =" +
|
"startTime=$startTime (time running =" +
|
||||||
" ${systemClock.elapsedRealtime() - startTime}ms)"
|
" ${systemClock.elapsedRealtime() - startTime}ms)"
|
||||||
)
|
)
|
||||||
pw.println("tokens: [")
|
pw.println("fgs tokens: [")
|
||||||
pw.indentIfPossible {
|
pw.indentIfPossible {
|
||||||
for (token in tokens) {
|
for (token in fgsTokens) {
|
||||||
pw.println("$token")
|
pw.println("$token")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pw.println("job summaries: [")
|
||||||
|
pw.indentIfPossible {
|
||||||
|
for (summary in jobSummaries) {
|
||||||
|
pw.println("$summary")
|
||||||
|
}
|
||||||
|
}
|
||||||
pw.println("]")
|
pw.println("]")
|
||||||
}
|
}
|
||||||
pw.println("]")
|
pw.println("]")
|
||||||
@@ -724,13 +850,13 @@ class FgsManagerControllerImpl @Inject constructor(
|
|||||||
synchronized(lock) {
|
synchronized(lock) {
|
||||||
pw.println("current user profiles = $currentProfileIds")
|
pw.println("current user profiles = $currentProfileIds")
|
||||||
pw.println("newChangesSinceDialogWasShown=$newChangesSinceDialogWasDismissed")
|
pw.println("newChangesSinceDialogWasShown=$newChangesSinceDialogWasDismissed")
|
||||||
pw.println("Running service tokens: [")
|
pw.println("Running task identifiers: [")
|
||||||
pw.indentIfPossible {
|
pw.indentIfPossible {
|
||||||
runningServiceTokens.forEach { (userPackage, startTimeAndTokens) ->
|
runningTaskIdentifiers.forEach { (userPackage, startTimeAndIdentifiers) ->
|
||||||
pw.println("{")
|
pw.println("{")
|
||||||
pw.indentIfPossible {
|
pw.indentIfPossible {
|
||||||
userPackage.dump(pw)
|
userPackage.dump(pw)
|
||||||
startTimeAndTokens.dump(pw)
|
startTimeAndIdentifiers.dump(pw)
|
||||||
}
|
}
|
||||||
pw.println("}")
|
pw.println("}")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.android.systemui.qs;
|
|||||||
|
|
||||||
import static android.os.PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE;
|
import static android.os.PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.ArgumentMatchers.argThat;
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
@@ -29,6 +30,9 @@ import static org.mockito.Mockito.verify;
|
|||||||
|
|
||||||
import android.app.IActivityManager;
|
import android.app.IActivityManager;
|
||||||
import android.app.IForegroundServiceObserver;
|
import android.app.IForegroundServiceObserver;
|
||||||
|
import android.app.job.IUserVisibleJobObserver;
|
||||||
|
import android.app.job.JobScheduler;
|
||||||
|
import android.app.job.UserVisibleJobSummary;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -59,6 +63,7 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.ArgumentMatchers;
|
import org.mockito.ArgumentMatchers;
|
||||||
|
import org.mockito.InOrder;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
@@ -79,6 +84,8 @@ public class FgsManagerControllerTest extends SysuiTestCase {
|
|||||||
@Mock
|
@Mock
|
||||||
IActivityManager mIActivityManager;
|
IActivityManager mIActivityManager;
|
||||||
@Mock
|
@Mock
|
||||||
|
JobScheduler mJobScheduler;
|
||||||
|
@Mock
|
||||||
PackageManager mPackageManager;
|
PackageManager mPackageManager;
|
||||||
@Mock
|
@Mock
|
||||||
UserTracker mUserTracker;
|
UserTracker mUserTracker;
|
||||||
@@ -92,8 +99,10 @@ public class FgsManagerControllerTest extends SysuiTestCase {
|
|||||||
private FgsManagerController mFmc;
|
private FgsManagerController mFmc;
|
||||||
|
|
||||||
private IForegroundServiceObserver mIForegroundServiceObserver;
|
private IForegroundServiceObserver mIForegroundServiceObserver;
|
||||||
|
private IUserVisibleJobObserver mIUserVisibleJobObserver;
|
||||||
private UserTracker.Callback mUserTrackerCallback;
|
private UserTracker.Callback mUserTrackerCallback;
|
||||||
private BroadcastReceiver mShowFgsManagerReceiver;
|
private BroadcastReceiver mShowFgsManagerReceiver;
|
||||||
|
private InOrder mJobSchedulerInOrder;
|
||||||
|
|
||||||
private List<UserInfo> mUserProfiles;
|
private List<UserInfo> mUserProfiles;
|
||||||
|
|
||||||
@@ -111,6 +120,8 @@ public class FgsManagerControllerTest extends SysuiTestCase {
|
|||||||
mUserProfiles = new ArrayList<>();
|
mUserProfiles = new ArrayList<>();
|
||||||
Mockito.doReturn(mUserProfiles).when(mUserTracker).getUserProfiles();
|
Mockito.doReturn(mUserProfiles).when(mUserTracker).getUserProfiles();
|
||||||
|
|
||||||
|
mJobSchedulerInOrder = Mockito.inOrder(mJobScheduler);
|
||||||
|
|
||||||
mFmc = createFgsManagerController();
|
mFmc = createFgsManagerController();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,6 +142,52 @@ public class FgsManagerControllerTest extends SysuiTestCase {
|
|||||||
Assert.assertEquals(0, mFmc.getNumRunningPackages());
|
Assert.assertEquals(0, mFmc.getNumRunningPackages());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNumPackages_jobs() throws RemoteException {
|
||||||
|
setUserProfiles(0);
|
||||||
|
setShowUserVisibleJobs(true);
|
||||||
|
|
||||||
|
UserVisibleJobSummary j1 = new UserVisibleJobSummary(0, 0, "pkg1", 0);
|
||||||
|
UserVisibleJobSummary j2 = new UserVisibleJobSummary(1, 0, "pkg2", 1);
|
||||||
|
Assert.assertEquals(0, mFmc.getNumRunningPackages());
|
||||||
|
mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j1, true);
|
||||||
|
Assert.assertEquals(1, mFmc.getNumRunningPackages());
|
||||||
|
mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j2, true);
|
||||||
|
Assert.assertEquals(2, mFmc.getNumRunningPackages());
|
||||||
|
mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j1, false);
|
||||||
|
Assert.assertEquals(1, mFmc.getNumRunningPackages());
|
||||||
|
mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j2, false);
|
||||||
|
Assert.assertEquals(0, mFmc.getNumRunningPackages());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNumPackages_FgsAndJobs() throws RemoteException {
|
||||||
|
setUserProfiles(0);
|
||||||
|
setShowUserVisibleJobs(true);
|
||||||
|
|
||||||
|
Binder b1 = new Binder();
|
||||||
|
Binder b2 = new Binder();
|
||||||
|
UserVisibleJobSummary j1 = new UserVisibleJobSummary(0, 0, "pkg1", 0);
|
||||||
|
UserVisibleJobSummary j3 = new UserVisibleJobSummary(1, 0, "pkg3", 1);
|
||||||
|
Assert.assertEquals(0, mFmc.getNumRunningPackages());
|
||||||
|
mIForegroundServiceObserver.onForegroundStateChanged(b1, "pkg1", 0, true);
|
||||||
|
Assert.assertEquals(1, mFmc.getNumRunningPackages());
|
||||||
|
mIForegroundServiceObserver.onForegroundStateChanged(b2, "pkg2", 0, true);
|
||||||
|
Assert.assertEquals(2, mFmc.getNumRunningPackages());
|
||||||
|
mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j1, true);
|
||||||
|
Assert.assertEquals(2, mFmc.getNumRunningPackages());
|
||||||
|
mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j3, true);
|
||||||
|
Assert.assertEquals(3, mFmc.getNumRunningPackages());
|
||||||
|
mIForegroundServiceObserver.onForegroundStateChanged(b2, "pkg2", 0, false);
|
||||||
|
Assert.assertEquals(2, mFmc.getNumRunningPackages());
|
||||||
|
mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j3, false);
|
||||||
|
Assert.assertEquals(1, mFmc.getNumRunningPackages());
|
||||||
|
mIForegroundServiceObserver.onForegroundStateChanged(b1, "pkg1", 0, false);
|
||||||
|
Assert.assertEquals(1, mFmc.getNumRunningPackages());
|
||||||
|
mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j1, false);
|
||||||
|
Assert.assertEquals(0, mFmc.getNumRunningPackages());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNumPackagesDoesNotChangeWhenSecondFgsIsStarted() throws RemoteException {
|
public void testNumPackagesDoesNotChangeWhenSecondFgsIsStarted() throws RemoteException {
|
||||||
setUserProfiles(0);
|
setUserProfiles(0);
|
||||||
@@ -243,6 +300,91 @@ public class FgsManagerControllerTest extends SysuiTestCase {
|
|||||||
Assert.assertEquals(0, mFmc.visibleButtonsCount());
|
Assert.assertEquals(0, mFmc.visibleButtonsCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShowUserVisibleJobsOnCreation() {
|
||||||
|
// Test when the default is on.
|
||||||
|
mDeviceConfigProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||||
|
SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS,
|
||||||
|
"true", false);
|
||||||
|
FgsManagerController fmc = new FgsManagerControllerImpl(
|
||||||
|
mContext,
|
||||||
|
mMainExecutor,
|
||||||
|
mBackgroundExecutor,
|
||||||
|
mSystemClock,
|
||||||
|
mIActivityManager,
|
||||||
|
mJobScheduler,
|
||||||
|
mPackageManager,
|
||||||
|
mUserTracker,
|
||||||
|
mDeviceConfigProxyFake,
|
||||||
|
mDialogLaunchAnimator,
|
||||||
|
mBroadcastDispatcher,
|
||||||
|
mDumpManager
|
||||||
|
);
|
||||||
|
fmc.init();
|
||||||
|
Assert.assertTrue(fmc.getIncludesUserVisibleJobs());
|
||||||
|
ArgumentCaptor<IUserVisibleJobObserver> iUserVisibleJobObserverArgumentCaptor =
|
||||||
|
ArgumentCaptor.forClass(IUserVisibleJobObserver.class);
|
||||||
|
mJobSchedulerInOrder.verify(mJobScheduler)
|
||||||
|
.registerUserVisibleJobObserver(iUserVisibleJobObserverArgumentCaptor.capture());
|
||||||
|
Assert.assertNotNull(iUserVisibleJobObserverArgumentCaptor.getValue());
|
||||||
|
|
||||||
|
// Test when the default is off.
|
||||||
|
mDeviceConfigProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||||
|
SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS,
|
||||||
|
"false", false);
|
||||||
|
fmc = new FgsManagerControllerImpl(
|
||||||
|
mContext,
|
||||||
|
mMainExecutor,
|
||||||
|
mBackgroundExecutor,
|
||||||
|
mSystemClock,
|
||||||
|
mIActivityManager,
|
||||||
|
mJobScheduler,
|
||||||
|
mPackageManager,
|
||||||
|
mUserTracker,
|
||||||
|
mDeviceConfigProxyFake,
|
||||||
|
mDialogLaunchAnimator,
|
||||||
|
mBroadcastDispatcher,
|
||||||
|
mDumpManager
|
||||||
|
);
|
||||||
|
fmc.init();
|
||||||
|
Assert.assertFalse(fmc.getIncludesUserVisibleJobs());
|
||||||
|
mJobSchedulerInOrder.verify(mJobScheduler, never()).registerUserVisibleJobObserver(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShowUserVisibleJobsToggling() throws Exception {
|
||||||
|
setUserProfiles(0);
|
||||||
|
setShowUserVisibleJobs(true);
|
||||||
|
|
||||||
|
// pkg1 has only job
|
||||||
|
// pkg2 has both job and fgs
|
||||||
|
// pkg3 has only fgs
|
||||||
|
UserVisibleJobSummary j1 = new UserVisibleJobSummary(0, 0, "pkg1", 0);
|
||||||
|
UserVisibleJobSummary j2 = new UserVisibleJobSummary(1, 0, "pkg2", 1);
|
||||||
|
Binder b2 = new Binder();
|
||||||
|
Binder b3 = new Binder();
|
||||||
|
|
||||||
|
Assert.assertEquals(0, mFmc.getNumRunningPackages());
|
||||||
|
mIForegroundServiceObserver.onForegroundStateChanged(b2, "pkg2", 0, true);
|
||||||
|
mIForegroundServiceObserver.onForegroundStateChanged(b3, "pkg3", 0, true);
|
||||||
|
Assert.assertEquals(2, mFmc.getNumRunningPackages());
|
||||||
|
mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j1, true);
|
||||||
|
mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j2, true);
|
||||||
|
Assert.assertEquals(3, mFmc.getNumRunningPackages());
|
||||||
|
|
||||||
|
// Turn off the flag, confirm the number of packages is updated properly.
|
||||||
|
setShowUserVisibleJobs(false);
|
||||||
|
// Only pkg1 should be removed since the other two have fgs
|
||||||
|
Assert.assertEquals(2, mFmc.getNumRunningPackages());
|
||||||
|
|
||||||
|
setShowUserVisibleJobs(true);
|
||||||
|
|
||||||
|
Assert.assertEquals(2, mFmc.getNumRunningPackages());
|
||||||
|
mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j1, true);
|
||||||
|
mIUserVisibleJobObserver.onUserVisibleJobStateChanged(j2, true);
|
||||||
|
Assert.assertEquals(3, mFmc.getNumRunningPackages());
|
||||||
|
}
|
||||||
|
|
||||||
private void setShowStopButtonForUserAllowlistedApps(boolean enable) {
|
private void setShowStopButtonForUserAllowlistedApps(boolean enable) {
|
||||||
mDeviceConfigProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
|
mDeviceConfigProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||||
SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS,
|
SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS,
|
||||||
@@ -251,6 +393,33 @@ public class FgsManagerControllerTest extends SysuiTestCase {
|
|||||||
mBackgroundExecutor.runAllReady();
|
mBackgroundExecutor.runAllReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setShowUserVisibleJobs(boolean enable) {
|
||||||
|
if (mFmc.getIncludesUserVisibleJobs() == enable) {
|
||||||
|
// No change.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDeviceConfigProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||||
|
SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS,
|
||||||
|
enable ? "true" : "false", false);
|
||||||
|
mBackgroundExecutor.advanceClockToLast();
|
||||||
|
mBackgroundExecutor.runAllReady();
|
||||||
|
|
||||||
|
ArgumentCaptor<IUserVisibleJobObserver> iUserVisibleJobObserverArgumentCaptor =
|
||||||
|
ArgumentCaptor.forClass(IUserVisibleJobObserver.class);
|
||||||
|
if (enable) {
|
||||||
|
mJobSchedulerInOrder.verify(mJobScheduler).registerUserVisibleJobObserver(
|
||||||
|
iUserVisibleJobObserverArgumentCaptor.capture()
|
||||||
|
);
|
||||||
|
mIUserVisibleJobObserver = iUserVisibleJobObserverArgumentCaptor.getValue();
|
||||||
|
} else {
|
||||||
|
mJobSchedulerInOrder.verify(mJobScheduler).unregisterUserVisibleJobObserver(
|
||||||
|
eq(mIUserVisibleJobObserver)
|
||||||
|
);
|
||||||
|
mIUserVisibleJobObserver = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void setBackgroundRestrictionExemptionReason(String pkgName, int uid, int reason)
|
private void setBackgroundRestrictionExemptionReason(String pkgName, int uid, int reason)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Mockito.doReturn(uid)
|
Mockito.doReturn(uid)
|
||||||
@@ -275,6 +444,7 @@ public class FgsManagerControllerTest extends SysuiTestCase {
|
|||||||
mBackgroundExecutor,
|
mBackgroundExecutor,
|
||||||
mSystemClock,
|
mSystemClock,
|
||||||
mIActivityManager,
|
mIActivityManager,
|
||||||
|
mJobScheduler,
|
||||||
mPackageManager,
|
mPackageManager,
|
||||||
mUserTracker,
|
mUserTracker,
|
||||||
mDeviceConfigProxyFake,
|
mDeviceConfigProxyFake,
|
||||||
@@ -304,6 +474,15 @@ public class FgsManagerControllerTest extends SysuiTestCase {
|
|||||||
mUserTrackerCallback = userTrackerCallbackArgumentCaptor.getValue();
|
mUserTrackerCallback = userTrackerCallbackArgumentCaptor.getValue();
|
||||||
mShowFgsManagerReceiver = showFgsManagerReceiverArgumentCaptor.getValue();
|
mShowFgsManagerReceiver = showFgsManagerReceiverArgumentCaptor.getValue();
|
||||||
|
|
||||||
|
if (result.getIncludesUserVisibleJobs()) {
|
||||||
|
ArgumentCaptor<IUserVisibleJobObserver> iUserVisibleJobObserverArgumentCaptor =
|
||||||
|
ArgumentCaptor.forClass(IUserVisibleJobObserver.class);
|
||||||
|
mJobSchedulerInOrder.verify(mJobScheduler).registerUserVisibleJobObserver(
|
||||||
|
iUserVisibleJobObserverArgumentCaptor.capture()
|
||||||
|
);
|
||||||
|
mIUserVisibleJobObserver = iUserVisibleJobObserverArgumentCaptor.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ class FakeFgsManagerController(
|
|||||||
|
|
||||||
override val showFooterDot: MutableStateFlow<Boolean> = MutableStateFlow(showFooterDot)
|
override val showFooterDot: MutableStateFlow<Boolean> = MutableStateFlow(showFooterDot)
|
||||||
|
|
||||||
|
override var includesUserVisibleJobs = false
|
||||||
|
private set
|
||||||
|
|
||||||
private val numRunningPackagesListeners = LinkedHashSet<OnNumberOfPackagesChangedListener>()
|
private val numRunningPackagesListeners = LinkedHashSet<OnNumberOfPackagesChangedListener>()
|
||||||
private val dialogDismissedListeners = LinkedHashSet<OnDialogDismissedListener>()
|
private val dialogDismissedListeners = LinkedHashSet<OnDialogDismissedListener>()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user