summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobInfo.java11
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobParameters.java4
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java115
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java45
-rw-r--r--cmds/gpu_counter_producer/Android.bp26
-rw-r--r--cmds/gpu_counter_producer/OWNERS1
-rw-r--r--cmds/gpu_counter_producer/main.cpp160
-rw-r--r--core/java/android/app/WallpaperManager.java18
-rw-r--r--core/java/android/view/WindowMetrics.java2
-rw-r--r--core/java/android/window/BackNavigationInfo.java49
-rw-r--r--core/java/com/android/internal/os/anr/AnrLatencyTracker.java6
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java48
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java18
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java10
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java98
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java87
-rw-r--r--packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java2
-rw-r--r--services/core/java/com/android/server/SystemServerInitThreadPool.java6
-rw-r--r--services/core/java/com/android/server/Watchdog.java3
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java445
-rw-r--r--services/core/java/com/android/server/am/AppExitInfoTracker.java2
-rw-r--r--services/core/java/com/android/server/am/ProcessErrorStateRecord.java2
-rw-r--r--services/core/java/com/android/server/am/StackTracesDumpHelper.java483
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java3
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplayMapper.java9
-rw-r--r--services/core/java/com/android/server/pm/SuspendPackageHelper.java6
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java10
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java4
-rw-r--r--services/core/java/com/android/server/wm/AnrController.java4
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java21
31 files changed, 1030 insertions, 671 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 37ceb091f035..805dfafe5923 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -1382,12 +1382,6 @@ public class JobInfo implements Parcelable {
* Calling this method will override any requirements previously defined
* by {@link #setRequiredNetwork(NetworkRequest)}; you typically only
* want to call one of these methods.
- *
- * Starting in Android version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
- * an app must hold the {@link android.Manifest.permission#INTERNET} and
- * {@link android.Manifest.permission#ACCESS_NETWORK_STATE} permissions to
- * schedule a job that requires a network.
- *
* <p class="note">
* When your job executes in
* {@link JobService#onStartJob(JobParameters)}, be sure to use the
@@ -1444,11 +1438,6 @@ public class JobInfo implements Parcelable {
* otherwise you'll use the default network which may not meet this
* constraint.
*
- * Starting in Android version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
- * an app must hold the {@link android.Manifest.permission#INTERNET} and
- * {@link android.Manifest.permission#ACCESS_NETWORK_STATE} permissions to
- * schedule a job that requires a network.
- *
* @param networkRequest The detailed description of the kind of network
* this job requires, or {@code null} if no specific kind of
* network is required. Defining a {@link NetworkSpecifier}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index bf4f9a83b99c..32502eddc9f8 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -481,10 +481,6 @@ public class JobParameters implements Parcelable {
* such as allowing a {@link JobInfo#NETWORK_TYPE_UNMETERED} job to run over
* a metered network when there is a surplus of metered data available.
*
- * Starting in Android version {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
- * this will return {@code null} if the app does not hold the permissions specified in
- * {@link JobInfo.Builder#setRequiredNetwork(NetworkRequest)}.
- *
* @return the network that should be used to perform any network requests
* for this job, or {@code null} if this job didn't set any required
* network type or if the job executed when there was no available network to use.
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index d06596fa18aa..d94993d64995 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -23,7 +23,6 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
-import android.Manifest;
import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -191,14 +190,6 @@ public class JobSchedulerService extends com.android.server.SystemService
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
private static final long REQUIRE_NETWORK_CONSTRAINT_FOR_NETWORK_JOB_WORK_ITEMS = 241104082L;
- /**
- * Require the app to have the INTERNET and ACCESS_NETWORK_STATE permissions when scheduling
- * a job with a connectivity constraint.
- */
- @ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
- static final long REQUIRE_NETWORK_PERMISSIONS_FOR_CONNECTIVITY_JOBS = 271850009L;
-
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public static Clock sSystemClock = Clock.systemUTC();
@@ -308,14 +299,6 @@ public class JobSchedulerService extends com.android.server.SystemService
private final RemoteCallbackList<IUserVisibleJobObserver> mUserVisibleJobObservers =
new RemoteCallbackList<>();
- /**
- * Cache of grant status of permissions, keyed by UID->PID->permission name. A missing value
- * means the state has not been queried.
- */
- @GuardedBy("mPermissionCache")
- private final SparseArray<SparseArrayMap<String, Boolean>> mPermissionCache =
- new SparseArray<>();
-
private final CountQuotaTracker mQuotaTracker;
private static final String QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG = ".schedulePersisted()";
private static final String QUOTA_TRACKER_SCHEDULE_LOGGED =
@@ -1059,10 +1042,6 @@ public class JobSchedulerService extends com.android.server.SystemService
final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
- synchronized (mPermissionCache) {
- // Something changed. Better clear the cached permission set.
- mPermissionCache.remove(pkgUid);
- }
// Purge the app's jobs if the whole package was just disabled. When this is
// the case the component name will be a bare package name.
if (pkgName != null && pkgUid != -1) {
@@ -1127,19 +1106,17 @@ public class JobSchedulerService extends com.android.server.SystemService
Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
}
} else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
- synchronized (mPermissionCache) {
- // Something changed. Better clear the cached permission set.
- mPermissionCache.remove(pkgUid);
- }
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
synchronized (mLock) {
- mUidToPackageCache.remove(pkgUid);
+ mUidToPackageCache.remove(uid);
+ }
+ } else {
+ synchronized (mJobSchedulerStub.mPersistCache) {
+ mJobSchedulerStub.mPersistCache.remove(pkgUid);
}
}
} else if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
- synchronized (mPermissionCache) {
- mPermissionCache.remove(pkgUid);
- }
if (DEBUG) {
Slog.d(TAG, "Removing jobs for " + pkgName + " (uid=" + pkgUid + ")");
}
@@ -1178,14 +1155,6 @@ public class JobSchedulerService extends com.android.server.SystemService
}
}
mConcurrencyManager.onUserRemoved(userId);
- synchronized (mPermissionCache) {
- for (int u = mPermissionCache.size() - 1; u >= 0; --u) {
- final int uid = mPermissionCache.keyAt(u);
- if (userId == UserHandle.getUserId(uid)) {
- mPermissionCache.removeAt(u);
- }
- }
- }
} else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
// Has this package scheduled any jobs, such that we will take action
// if it were to be force-stopped?
@@ -3779,38 +3748,18 @@ public class JobSchedulerService extends com.android.server.SystemService
}
/**
- * Returns whether the app has the permission granted.
- * This currently only works for normal permissions and <b>DOES NOT</b> work for runtime
- * permissions.
- * TODO: handle runtime permissions
- */
- private boolean hasPermission(int uid, int pid, @NonNull String permission) {
- synchronized (mPermissionCache) {
- SparseArrayMap<String, Boolean> pidPermissions = mPermissionCache.get(uid);
- if (pidPermissions == null) {
- pidPermissions = new SparseArrayMap<>();
- mPermissionCache.put(uid, pidPermissions);
- }
- final Boolean cached = pidPermissions.get(pid, permission);
- if (cached != null) {
- return cached;
- }
-
- final int result = getContext().checkPermission(permission, pid, uid);
- final boolean permissionGranted = (result == PackageManager.PERMISSION_GRANTED);
- pidPermissions.add(pid, permission, permissionGranted);
- return permissionGranted;
- }
- }
-
- /**
* Binder stub trampoline implementation
*/
final class JobSchedulerStub extends IJobScheduler.Stub {
+ /**
+ * Cache determination of whether a given app can persist jobs
+ * key is uid of the calling app; value is undetermined/true/false
+ */
+ private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
+
// Enforce that only the app itself (or shared uid participant) can schedule a
// job that runs one of the app's services, as well as verifying that the
// named service properly requires the BIND_JOB_SERVICE permission
- // TODO(141645789): merge enforceValidJobRequest() with validateJob()
private void enforceValidJobRequest(int uid, int pid, JobInfo job) {
final PackageManager pm = getContext()
.createContextAsUser(UserHandle.getUserHandleForUid(uid), 0)
@@ -3835,33 +3784,31 @@ public class JobSchedulerService extends com.android.server.SystemService
throw new IllegalArgumentException(
"Tried to schedule job for non-existent component: " + service);
}
- // If we get this far we're good to go; all we need to do now is check
- // whether the app is allowed to persist its scheduled work.
if (job.isPersisted() && !canPersistJobs(pid, uid)) {
throw new IllegalArgumentException("Requested job cannot be persisted without"
+ " holding android.permission.RECEIVE_BOOT_COMPLETED permission");
}
- if (job.getRequiredNetwork() != null
- && CompatChanges.isChangeEnabled(
- REQUIRE_NETWORK_PERMISSIONS_FOR_CONNECTIVITY_JOBS, uid)) {
- // All networking, including with the local network and even local to the device,
- // requires the INTERNET permission.
- if (!hasPermission(uid, pid, Manifest.permission.INTERNET)) {
- throw new SecurityException(Manifest.permission.INTERNET
- + " required for jobs with a connectivity constraint");
- }
- if (!hasPermission(uid, pid, Manifest.permission.ACCESS_NETWORK_STATE)) {
- throw new SecurityException(Manifest.permission.ACCESS_NETWORK_STATE
- + " required for jobs with a connectivity constraint");
- }
- }
}
private boolean canPersistJobs(int pid, int uid) {
- // Persisting jobs is tantamount to running at boot, so we permit
- // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
- // permission
- return hasPermission(uid, pid, Manifest.permission.RECEIVE_BOOT_COMPLETED);
+ // If we get this far we're good to go; all we need to do now is check
+ // whether the app is allowed to persist its scheduled work.
+ final boolean canPersist;
+ synchronized (mPersistCache) {
+ Boolean cached = mPersistCache.get(uid);
+ if (cached != null) {
+ canPersist = cached.booleanValue();
+ } else {
+ // Persisting jobs is tantamount to running at boot, so we permit
+ // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
+ // permission
+ int result = getContext().checkPermission(
+ android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
+ canPersist = (result == PackageManager.PERMISSION_GRANTED);
+ mPersistCache.put(uid, canPersist);
+ }
+ }
+ return canPersist;
}
private int validateJob(@NonNull JobInfo job, int callingUid, int callingPid,
@@ -4080,8 +4027,6 @@ public class JobSchedulerService extends com.android.server.SystemService
+ " not permitted to schedule jobs for other apps");
}
- enforceValidJobRequest(callerUid, callerPid, job);
-
int result = validateJob(job, callerUid, callerPid, userId, packageName, null);
if (result != JobScheduler.RESULT_SUCCESS) {
return result;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 1e2ef7755664..b080bf31fed4 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -21,7 +21,6 @@ import static android.app.job.JobInfo.getPriorityString;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
-import android.Manifest;
import android.annotation.BytesLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -40,7 +39,6 @@ import android.compat.annotation.EnabledAfter;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.PermissionChecker;
import android.content.ServiceConnection;
import android.net.Network;
import android.net.Uri;
@@ -341,13 +339,12 @@ public final class JobServiceContext implements ServiceConnection {
job.changedAuthorities.toArray(triggeredAuthorities);
}
final JobInfo ji = job.getJob();
- final Network passedNetwork = canGetNetworkInformation(job) ? job.network : null;
mParams = new JobParameters(mRunningCallback, job.getNamespace(), job.getJobId(),
ji.getExtras(),
ji.getTransientExtras(), ji.getClipData(), ji.getClipGrantFlags(),
isDeadlineExpired, job.shouldTreatAsExpeditedJob(),
job.shouldTreatAsUserInitiatedJob(), triggeredUris, triggeredAuthorities,
- passedNetwork);
+ job.network);
mExecutionStartTimeElapsed = sElapsedRealtimeClock.millis();
mMinExecutionGuaranteeMillis = mService.getMinJobExecutionGuaranteeMs(job);
mMaxExecutionTimeMillis =
@@ -507,37 +504,6 @@ public final class JobServiceContext implements ServiceConnection {
}
}
- private boolean canGetNetworkInformation(@NonNull JobStatus job) {
- if (job.getJob().getRequiredNetwork() == null) {
- // The job never had a network constraint, so we're not going to give it a network
- // object. Add this check as an early return to avoid wasting cycles doing permission
- // checks for this job.
- return false;
- }
- // The calling app is doing the work, so use its UID, not the source UID.
- final int uid = job.getUid();
- if (CompatChanges.isChangeEnabled(
- JobSchedulerService.REQUIRE_NETWORK_PERMISSIONS_FOR_CONNECTIVITY_JOBS, uid)) {
- final String pkgName = job.getServiceComponent().getPackageName();
- if (!hasPermissionForDelivery(uid, pkgName, Manifest.permission.INTERNET)) {
- return false;
- }
- if (!hasPermissionForDelivery(uid, pkgName, Manifest.permission.ACCESS_NETWORK_STATE)) {
- return false;
- }
- }
-
- return true;
- }
-
- private boolean hasPermissionForDelivery(int uid, @NonNull String pkgName,
- @NonNull String permission) {
- final int result = PermissionChecker.checkPermissionForDataDelivery(mContext, permission,
- PermissionChecker.PID_UNKNOWN, uid, pkgName, /* attributionTag */ null,
- "network info via JS");
- return result == PermissionChecker.PERMISSION_GRANTED;
- }
-
@EconomicPolicy.AppAction
private static int getStartActionId(@NonNull JobStatus job) {
switch (job.getEffectivePriority()) {
@@ -637,15 +603,6 @@ public final class JobServiceContext implements ServiceConnection {
}
void informOfNetworkChangeLocked(Network newNetwork) {
- if (newNetwork != null && mRunningJob != null && !canGetNetworkInformation(mRunningJob)) {
- // The app can't get network information, so there's no point informing it of network
- // changes. This case may happen if an app had scheduled network job and then
- // started targeting U+ without requesting the required network permissions.
- if (DEBUG) {
- Slog.d(TAG, "Skipping network change call because of missing permissions");
- }
- return;
- }
if (mVerb != VERB_EXECUTING) {
Slog.w(TAG, "Sending onNetworkChanged for a job that isn't started. " + mRunningJob);
if (mVerb == VERB_BINDING || mVerb == VERB_STARTING) {
diff --git a/cmds/gpu_counter_producer/Android.bp b/cmds/gpu_counter_producer/Android.bp
new file mode 100644
index 000000000000..5b118ce62679
--- /dev/null
+++ b/cmds/gpu_counter_producer/Android.bp
@@ -0,0 +1,26 @@
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+cc_binary {
+ name: "gpu_counter_producer",
+
+ srcs: ["main.cpp"],
+
+ shared_libs: [
+ "libdl",
+ "liblog",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ "-fPIE",
+ "-pie",
+ ],
+
+ soc_specific: true,
+}
diff --git a/cmds/gpu_counter_producer/OWNERS b/cmds/gpu_counter_producer/OWNERS
new file mode 100644
index 000000000000..892c2fb81cde
--- /dev/null
+++ b/cmds/gpu_counter_producer/OWNERS
@@ -0,0 +1 @@
+pmuetschard@google.com
diff --git a/cmds/gpu_counter_producer/main.cpp b/cmds/gpu_counter_producer/main.cpp
new file mode 100644
index 000000000000..1054cba74a6b
--- /dev/null
+++ b/cmds/gpu_counter_producer/main.cpp
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "gpu_counters"
+
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <log/log.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define _LOG(level, msg, ...) \
+ do { \
+ fprintf(stderr, #level ": " msg "\n", ##__VA_ARGS__); \
+ ALOG##level(msg, ##__VA_ARGS__); \
+ } while (false)
+
+#define LOG_ERR(msg, ...) _LOG(E, msg, ##__VA_ARGS__)
+#define LOG_WARN(msg, ...) _LOG(W, msg, ##__VA_ARGS__)
+#define LOG_INFO(msg, ...) _LOG(I, msg, ##__VA_ARGS__)
+
+#define NELEM(x) (sizeof(x) / sizeof(x[0]))
+
+typedef void (*FN_PTR)(void);
+
+const char* kProducerPaths[] = {
+ "libgpudataproducer.so",
+};
+const char* kPidFileName = "/data/local/tmp/gpu_counter_producer.pid";
+
+static FN_PTR loadLibrary(const char* lib) {
+ char* error;
+
+ LOG_INFO("Trying %s", lib);
+ void* handle = dlopen(lib, RTLD_GLOBAL);
+ if ((error = dlerror()) != nullptr || handle == nullptr) {
+ LOG_WARN("Error loading lib: %s", error);
+ return nullptr;
+ }
+
+ FN_PTR startFunc = (FN_PTR)dlsym(handle, "start");
+ if ((error = dlerror()) != nullptr) {
+ LOG_ERR("Error looking for start symbol: %s", error);
+ dlclose(handle);
+ return nullptr;
+ }
+ return startFunc;
+}
+
+static void killExistingProcess() {
+ int fd = open(kPidFileName, O_RDONLY);
+ if (fd == -1) {
+ return;
+ }
+ char pidString[10];
+ if (read(fd, pidString, 10) > 0) {
+ int pid = -1;
+ sscanf(pidString, "%d", &pid);
+ if (pid > 0) {
+ kill(pid, SIGINT);
+ }
+ }
+ close(fd);
+}
+
+static bool writeToPidFile() {
+ killExistingProcess();
+ int fd = open(kPidFileName, O_CREAT | O_WRONLY | O_TRUNC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+ if (fd == -1) {
+ return false;
+ }
+ pid_t pid = getpid();
+ char pidString[10];
+ sprintf(pidString, "%d", pid);
+ write(fd, pidString, strlen(pidString));
+ close(fd);
+ return true;
+}
+
+static void clearPidFile() {
+ unlink(kPidFileName);
+}
+
+static void usage(const char* pname) {
+ fprintf(stderr,
+ "Starts the GPU hardware counter profiling Perfetto data producer.\n\n"
+ "usage: %s [-hf]\n"
+ " -f: run in the foreground.\n"
+ " -h: this message.\n",
+ pname);
+}
+
+// Program to load the GPU Perfetto producer .so and call start().
+int main(int argc, char** argv) {
+ const char* pname = argv[0];
+ bool foreground = false;
+ int c;
+ while ((c = getopt(argc, argv, "fh")) != -1) {
+ switch (c) {
+ case 'f':
+ foreground = true;
+ break;
+ case '?':
+ case ':':
+ case 'h':
+ usage(pname);
+ return 1;
+ }
+ }
+
+ if (optind < argc) {
+ usage(pname);
+ return 1;
+ }
+
+ if (!foreground) {
+ daemon(0, 0);
+ }
+
+ if (!writeToPidFile()) {
+ LOG_ERR("Could not open %s", kPidFileName);
+ return 1;
+ }
+
+ dlerror(); // Clear any possibly ignored previous error.
+ FN_PTR startFunc = nullptr;
+ for (int i = 0; startFunc == nullptr && i < NELEM(kProducerPaths); i++) {
+ startFunc = loadLibrary(kProducerPaths[i]);
+ }
+
+ if (startFunc == nullptr) {
+ LOG_ERR("Did not find the producer library");
+ LOG_ERR("LD_LIBRARY_PATH=%s", getenv("LD_LIBRARY_PATH"));
+ clearPidFile();
+ return 1;
+ }
+
+ LOG_INFO("Calling start at %p", startFunc);
+ (*startFunc)();
+ LOG_WARN("Producer has exited.");
+
+ clearPidFile();
+ return 0;
+}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 540342b03f1a..4d55fee35506 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -858,6 +858,8 @@ public class WallpaperManager {
* (some versions of T may throw a {@code SecurityException}).</li>
* <li>From version U, this method should not be used
* and will always throw a @code SecurityException}.</li>
+ * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
+ * can still access the real wallpaper on all versions. </li>
* </ul>
* <br>
*
@@ -893,6 +895,8 @@ public class WallpaperManager {
* (some versions of T may throw a {@code SecurityException}).</li>
* <li>From version U, this method should not be used
* and will always throw a @code SecurityException}.</li>
+ * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
+ * can still access the real wallpaper on all versions. </li>
* </ul>
* <br>
*
@@ -1147,6 +1151,8 @@ public class WallpaperManager {
* (some versions of T may throw a {@code SecurityException}).</li>
* <li>From version U, this method should not be used
* and will always throw a @code SecurityException}.</li>
+ * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
+ * can still access the real wallpaper on all versions. </li>
* </ul>
* <br>
*
@@ -1176,6 +1182,8 @@ public class WallpaperManager {
* (some versions of T may throw a {@code SecurityException}).</li>
* <li>From version U, this method should not be used
* and will always throw a @code SecurityException}.</li>
+ * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
+ * can still access the real wallpaper on all versions. </li>
* </ul>
* <br>
*
@@ -1214,6 +1222,8 @@ public class WallpaperManager {
* (some versions of T may throw a {@code SecurityException}).</li>
* <li>From version U, this method should not be used
* and will always throw a @code SecurityException}.</li>
+ * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
+ * can still access the real wallpaper on all versions. </li>
* </ul>
* <br>
*
@@ -1247,6 +1257,8 @@ public class WallpaperManager {
* (some versions of T may throw a {@code SecurityException}).</li>
* <li>From version U, this method should not be used
* and will always throw a @code SecurityException}.</li>
+ * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
+ * can still access the real wallpaper on all versions. </li>
* </ul>
* <br>
*
@@ -1287,6 +1299,8 @@ public class WallpaperManager {
* (some versions of T may throw a {@code SecurityException}).</li>
* <li>From version U, this method should not be used
* and will always throw a @code SecurityException}.</li>
+ * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
+ * can still access the real wallpaper on all versions. </li>
* </ul>
* <br>
*
@@ -1314,6 +1328,8 @@ public class WallpaperManager {
* (some versions of T may throw a {@code SecurityException}).</li>
* <li>From version U, this method should not be used
* and will always throw a @code SecurityException}.</li>
+ * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
+ * can still access the real wallpaper on all versions. </li>
* </ul>
* <br>
*
@@ -1458,6 +1474,8 @@ public class WallpaperManager {
* (some versions of T may throw a {@code SecurityException}).</li>
* <li>From version U, this method should not be used
* and will always throw a @code SecurityException}.</li>
+ * <li> Apps with {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE}
+ * can still access the real wallpaper on all versions. </li>
* </ul>
* <br>
*
diff --git a/core/java/android/view/WindowMetrics.java b/core/java/android/view/WindowMetrics.java
index b74b80eeec2c..7ad43c76efaa 100644
--- a/core/java/android/view/WindowMetrics.java
+++ b/core/java/android/view/WindowMetrics.java
@@ -162,7 +162,7 @@ public final class WindowMetrics {
return WindowMetrics.class.getSimpleName() + ":{"
+ "bounds=" + mBounds
+ ", windowInsets=" + mWindowInsets
- + ", density" + mDensity
+ + ", density=" + mDensity
+ "}";
}
}
diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java
index 514059456279..e0ee68337061 100644
--- a/core/java/android/window/BackNavigationInfo.java
+++ b/core/java/android/window/BackNavigationInfo.java
@@ -16,6 +16,8 @@
package android.window;
+import android.annotation.AnimRes;
+import android.annotation.ColorInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -245,6 +247,9 @@ public final class BackNavigationInfo implements Parcelable {
public static final class CustomAnimationInfo implements Parcelable {
private final String mPackageName;
private int mWindowAnimations;
+ @AnimRes private int mCustomExitAnim;
+ @AnimRes private int mCustomEnterAnim;
+ @ColorInt private int mCustomBackground;
/**
* The package name of the windowAnimations.
@@ -261,6 +266,27 @@ public final class BackNavigationInfo implements Parcelable {
return mWindowAnimations;
}
+ /**
+ * The exit animation resource Id of customize activity transition.
+ */
+ public int getCustomExitAnim() {
+ return mCustomExitAnim;
+ }
+
+ /**
+ * The entering animation resource Id of customize activity transition.
+ */
+ public int getCustomEnterAnim() {
+ return mCustomEnterAnim;
+ }
+
+ /**
+ * The background color of customize activity transition.
+ */
+ public int getCustomBackground() {
+ return mCustomBackground;
+ }
+
public CustomAnimationInfo(@NonNull String packageName) {
this.mPackageName = packageName;
}
@@ -274,11 +300,17 @@ public final class BackNavigationInfo implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString8(mPackageName);
dest.writeInt(mWindowAnimations);
+ dest.writeInt(mCustomEnterAnim);
+ dest.writeInt(mCustomExitAnim);
+ dest.writeInt(mCustomBackground);
}
private CustomAnimationInfo(@NonNull Parcel in) {
mPackageName = in.readString8();
mWindowAnimations = in.readInt();
+ mCustomEnterAnim = in.readInt();
+ mCustomExitAnim = in.readInt();
+ mCustomBackground = in.readInt();
}
@Override
@@ -349,10 +381,25 @@ public final class BackNavigationInfo implements Parcelable {
* Set windowAnimations for customize animation.
*/
public Builder setWindowAnimations(String packageName, int windowAnimations) {
- mCustomAnimationInfo = new CustomAnimationInfo(packageName);
+ if (mCustomAnimationInfo == null) {
+ mCustomAnimationInfo = new CustomAnimationInfo(packageName);
+ }
mCustomAnimationInfo.mWindowAnimations = windowAnimations;
return this;
}
+ /**
+ * Set resources ids for customize activity animation.
+ */
+ public Builder setCustomAnimation(String packageName, @AnimRes int enterResId,
+ @AnimRes int exitResId, @ColorInt int backgroundColor) {
+ if (mCustomAnimationInfo == null) {
+ mCustomAnimationInfo = new CustomAnimationInfo(packageName);
+ }
+ mCustomAnimationInfo.mCustomExitAnim = exitResId;
+ mCustomAnimationInfo.mCustomEnterAnim = enterResId;
+ mCustomAnimationInfo.mCustomBackground = backgroundColor;
+ return this;
+ }
/**
* Builds and returns an instance of {@link BackNavigationInfo}
diff --git a/core/java/com/android/internal/os/anr/AnrLatencyTracker.java b/core/java/com/android/internal/os/anr/AnrLatencyTracker.java
index 6a6165687981..096d1cd212be 100644
--- a/core/java/com/android/internal/os/anr/AnrLatencyTracker.java
+++ b/core/java/com/android/internal/os/anr/AnrLatencyTracker.java
@@ -137,14 +137,14 @@ public class AnrLatencyTracker implements AutoCloseable {
close();
}
- /** Records the start of ActivityManagerService#dumpStackTraces. */
+ /** Records the start of StackTracesDumpHelper#dumpStackTraces. */
public void dumpStackTracesStarted() {
mDumpStackTracesStartUptime = getUptimeMillis();
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER,
"dumpStackTraces()");
}
- /** Records the end of ActivityManagerService#dumpStackTraces. */
+ /** Records the end of StackTracesDumpHelper#dumpStackTraces. */
public void dumpStackTracesEnded() {
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -328,7 +328,7 @@ public class AnrLatencyTracker implements AutoCloseable {
anrSkipped("appNotResponding");
}
- /** Records a skipped ANR in ActivityManagerService#dumpStackTraces. */
+ /** Records a skipped ANR in StackTracesDumpHelper#dumpStackTraces. */
public void anrSkippedDumpStackTraces() {
anrSkipped("dumpStackTraces");
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 404429ad41d3..89f4890c254e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -40,7 +40,9 @@ import static androidx.window.extensions.embedding.SplitContainer.isStickyPlaceh
import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenAdjacent;
import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenStacked;
import static androidx.window.extensions.embedding.SplitPresenter.RESULT_EXPAND_FAILED_NO_TF_INFO;
+import static androidx.window.extensions.embedding.SplitPresenter.getActivitiesMinDimensionsPair;
import static androidx.window.extensions.embedding.SplitPresenter.getActivityIntentMinDimensionsPair;
+import static androidx.window.extensions.embedding.SplitPresenter.getTaskWindowMetrics;
import static androidx.window.extensions.embedding.SplitPresenter.shouldShowSplit;
import android.app.Activity;
@@ -1037,9 +1039,15 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
final TaskFragmentContainer primaryContainer = getContainerWithActivity(
primaryActivity);
final SplitContainer splitContainer = getActiveSplitForContainer(primaryContainer);
- final WindowMetrics taskWindowMetrics = mPresenter.getTaskWindowMetrics(primaryActivity);
+ final TaskContainer.TaskProperties taskProperties = mPresenter
+ .getTaskProperties(primaryActivity);
+ final SplitAttributes calculatedSplitAttributes = mPresenter.computeSplitAttributes(
+ taskProperties, splitRule, splitRule.getDefaultSplitAttributes(),
+ getActivitiesMinDimensionsPair(primaryActivity, secondaryActivity));
if (splitContainer != null && primaryContainer == splitContainer.getPrimaryContainer()
- && canReuseContainer(splitRule, splitContainer.getSplitRule(), taskWindowMetrics)) {
+ && canReuseContainer(splitRule, splitContainer.getSplitRule(),
+ getTaskWindowMetrics(taskProperties.getConfiguration()),
+ calculatedSplitAttributes, splitContainer.getCurrentSplitAttributes())) {
// Can launch in the existing secondary container if the rules share the same
// presentation.
final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
@@ -1058,7 +1066,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
}
// Create new split pair.
- mPresenter.createNewSplitContainer(wct, primaryActivity, secondaryActivity, splitRule);
+ mPresenter.createNewSplitContainer(wct, primaryActivity, secondaryActivity, splitRule,
+ calculatedSplitAttributes);
return true;
}
@@ -1283,9 +1292,16 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
final TaskFragmentContainer existingContainer = getContainerWithActivity(primaryActivity);
final SplitContainer splitContainer = getActiveSplitForContainer(existingContainer);
- final WindowMetrics taskWindowMetrics = mPresenter.getTaskWindowMetrics(primaryActivity);
+ final TaskContainer.TaskProperties taskProperties = mPresenter
+ .getTaskProperties(primaryActivity);
+ final WindowMetrics taskWindowMetrics = getTaskWindowMetrics(
+ taskProperties.getConfiguration());
+ final SplitAttributes calculatedSplitAttributes = mPresenter.computeSplitAttributes(
+ taskProperties, splitRule, splitRule.getDefaultSplitAttributes(),
+ getActivityIntentMinDimensionsPair(primaryActivity, intent));
if (splitContainer != null && existingContainer == splitContainer.getPrimaryContainer()
- && (canReuseContainer(splitRule, splitContainer.getSplitRule(), taskWindowMetrics)
+ && (canReuseContainer(splitRule, splitContainer.getSplitRule(), taskWindowMetrics,
+ calculatedSplitAttributes, splitContainer.getCurrentSplitAttributes())
// TODO(b/231845476) we should always respect clearTop.
|| !respectClearTop)
&& mPresenter.expandSplitContainerIfNeeded(wct, splitContainer, primaryActivity,
@@ -1296,7 +1312,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
// Create a new TaskFragment to split with the primary activity for the new activity.
return mPresenter.createNewSplitWithEmptySideContainer(wct, primaryActivity, intent,
- splitRule);
+ splitRule, calculatedSplitAttributes);
}
/**
@@ -2273,21 +2289,29 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
/**
- * If the two rules have the same presentation, we can reuse the same {@link SplitContainer} if
- * there is any.
+ * If the two rules have the same presentation, and the calculated {@link SplitAttributes}
+ * matches the {@link SplitAttributes} of {@link SplitContainer}, we can reuse the same
+ * {@link SplitContainer} if there is any.
*/
private static boolean canReuseContainer(@NonNull SplitRule rule1, @NonNull SplitRule rule2,
- @NonNull WindowMetrics parentWindowMetrics) {
+ @NonNull WindowMetrics parentWindowMetrics,
+ @NonNull SplitAttributes calculatedSplitAttributes,
+ @NonNull SplitAttributes containerSplitAttributes) {
if (!isContainerReusableRule(rule1) || !isContainerReusableRule(rule2)) {
return false;
}
- return haveSamePresentation((SplitPairRule) rule1, (SplitPairRule) rule2,
- parentWindowMetrics);
+ return areRulesSamePresentation((SplitPairRule) rule1, (SplitPairRule) rule2,
+ parentWindowMetrics)
+ // Besides rules, we should also check whether the SplitContainer's splitAttributes
+ // matches the current splitAttributes or not. The splitAttributes may change
+ // if the app chooses different SplitAttributes calculator function before a new
+ // activity is started even they match the same splitRule.
+ && calculatedSplitAttributes.equals(containerSplitAttributes);
}
/** Whether the two rules have the same presentation. */
@VisibleForTesting
- static boolean haveSamePresentation(@NonNull SplitPairRule rule1,
+ static boolean areRulesSamePresentation(@NonNull SplitPairRule rule1,
@NonNull SplitPairRule rule2, @NonNull WindowMetrics parentWindowMetrics) {
if (rule1.getTag() != null || rule2.getTag() != null) {
// Tag must be unique if it is set. We don't want to reuse the container if the rules
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 040851181e92..20b6ae29939c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -174,12 +174,9 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
@NonNull
TaskFragmentContainer createNewSplitWithEmptySideContainer(
@NonNull WindowContainerTransaction wct, @NonNull Activity primaryActivity,
- @NonNull Intent secondaryIntent, @NonNull SplitPairRule rule) {
+ @NonNull Intent secondaryIntent, @NonNull SplitPairRule rule,
+ @NonNull SplitAttributes splitAttributes) {
final TaskProperties taskProperties = getTaskProperties(primaryActivity);
- final Pair<Size, Size> minDimensionsPair = getActivityIntentMinDimensionsPair(
- primaryActivity, secondaryIntent);
- final SplitAttributes splitAttributes = computeSplitAttributes(taskProperties, rule,
- rule.getDefaultSplitAttributes(), minDimensionsPair);
final Rect primaryRelBounds = getRelBoundsForPosition(POSITION_START, taskProperties,
splitAttributes);
final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
@@ -217,15 +214,12 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
* same container as the primary activity, a new container will be
* created and the activity will be re-parented to it.
* @param rule The split rule to be applied to the container.
+ * @param splitAttributes The {@link SplitAttributes} to apply
*/
void createNewSplitContainer(@NonNull WindowContainerTransaction wct,
@NonNull Activity primaryActivity, @NonNull Activity secondaryActivity,
- @NonNull SplitPairRule rule) {
+ @NonNull SplitPairRule rule, @NonNull SplitAttributes splitAttributes) {
final TaskProperties taskProperties = getTaskProperties(primaryActivity);
- final Pair<Size, Size> minDimensionsPair = getActivitiesMinDimensionsPair(primaryActivity,
- secondaryActivity);
- final SplitAttributes splitAttributes = computeSplitAttributes(taskProperties, rule,
- rule.getDefaultSplitAttributes(), minDimensionsPair);
final Rect primaryRelBounds = getRelBoundsForPosition(POSITION_START, taskProperties,
splitAttributes);
final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
@@ -654,7 +648,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
}
@NonNull
- private static Pair<Size, Size> getActivitiesMinDimensionsPair(
+ static Pair<Size, Size> getActivitiesMinDimensionsPair(
@NonNull Activity primaryActivity, @NonNull Activity secondaryActivity) {
return new Pair<>(getMinDimensions(primaryActivity), getMinDimensions(secondaryActivity));
}
@@ -1027,7 +1021,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
}
@NonNull
- private static WindowMetrics getTaskWindowMetrics(@NonNull Configuration taskConfiguration) {
+ static WindowMetrics getTaskWindowMetrics(@NonNull Configuration taskConfiguration) {
final Rect taskBounds = taskConfiguration.windowConfiguration.getBounds();
// TODO(b/190433398): Supply correct insets.
final float density = taskConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index cbdc262c0594..ff08782e8cd8 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -565,7 +565,6 @@ public class SplitControllerTest {
assertNotNull(mSplitController.getActiveSplitForContainers(primaryContainer, container));
assertTrue(primaryContainer.areLastRequestedBoundsEqual(null));
assertTrue(container.areLastRequestedBoundsEqual(null));
- assertEquals(container, mSplitController.getContainerWithActivity(secondaryActivity));
}
@Test
@@ -1008,9 +1007,8 @@ public class SplitControllerTest {
assertTrue(result);
assertSplitPair(primaryActivity, mActivity, true /* matchParentBounds */);
- assertEquals(mSplitController.getContainerWithActivity(secondaryActivity),
- mSplitController.getContainerWithActivity(mActivity));
- verify(mSplitPresenter, never()).createNewSplitContainer(any(), any(), any(), any());
+ assertTrue(mSplitController.getContainerWithActivity(mActivity)
+ .areLastRequestedBoundsEqual(new Rect()));
}
@Test
@@ -1215,7 +1213,7 @@ public class SplitControllerTest {
.build();
assertTrue("Rules must have same presentation if tags are null and has same properties.",
- SplitController.haveSamePresentation(splitRule1, splitRule2,
+ SplitController.areRulesSamePresentation(splitRule1, splitRule2,
new WindowMetrics(TASK_BOUNDS, WindowInsets.CONSUMED)));
splitRule2 = createSplitPairRuleBuilder(
@@ -1230,7 +1228,7 @@ public class SplitControllerTest {
assertFalse("Rules must have different presentations if tags are not equal regardless"
+ "of other properties",
- SplitController.haveSamePresentation(splitRule1, splitRule2,
+ SplitController.areRulesSamePresentation(splitRule1, splitRule2,
new WindowMetrics(TASK_BOUNDS, WindowInsets.CONSUMED)));
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
index 83301564d7a4..8dd13846ab3d 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
@@ -678,7 +678,8 @@ public class SplitPresenterTest {
.setShouldClearTop(false)
.build();
- mPresenter.createNewSplitContainer(mTransaction, mActivity, secondaryActivity, rule);
+ mPresenter.createNewSplitContainer(mTransaction, mActivity, secondaryActivity, rule,
+ SPLIT_ATTRIBUTES);
assertEquals(primaryTf, mController.getContainerWithActivity(mActivity));
final TaskFragmentContainer secondaryTf = mController.getContainerWithActivity(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java
index ae33b9445acd..4eaedd3136f1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java
@@ -25,7 +25,10 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Activity;
import android.content.Context;
+import android.graphics.Color;
import android.graphics.Rect;
import android.os.RemoteException;
import android.util.FloatProperty;
@@ -34,6 +37,7 @@ import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
+import android.view.WindowManager.LayoutParams;
import android.view.animation.Animation;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Transformation;
@@ -43,6 +47,7 @@ import android.window.BackNavigationInfo;
import android.window.BackProgressAnimator;
import android.window.IOnBackInvokedCallback;
+import com.android.internal.R;
import com.android.internal.dynamicanimation.animation.SpringAnimation;
import com.android.internal.dynamicanimation.animation.SpringForce;
import com.android.internal.policy.ScreenDecorationsUtils;
@@ -78,6 +83,7 @@ class CustomizeActivityAnimation {
final CustomAnimationLoader mCustomAnimationLoader;
private Animation mEnterAnimation;
private Animation mCloseAnimation;
+ private int mNextBackgroundColor;
final Transformation mTransformation = new Transformation();
private final Choreographer mChoreographer;
@@ -144,8 +150,9 @@ class CustomizeActivityAnimation {
// Draw background with task background color.
if (mEnteringTarget.taskInfo != null && mEnteringTarget.taskInfo.taskDescription != null) {
- mBackground.ensureBackground(
- mEnteringTarget.taskInfo.taskDescription.getBackgroundColor(), mTransaction);
+ mBackground.ensureBackground(mNextBackgroundColor == Color.TRANSPARENT
+ ? mEnteringTarget.taskInfo.taskDescription.getBackgroundColor()
+ : mNextBackgroundColor, mTransaction);
}
}
@@ -191,6 +198,7 @@ class CustomizeActivityAnimation {
mTransaction.apply();
mTransformation.clear();
mLatestProgress = 0;
+ mNextBackgroundColor = Color.TRANSPARENT;
if (mFinishCallback != null) {
try {
mFinishCallback.onAnimationFinished();
@@ -252,11 +260,11 @@ class CustomizeActivityAnimation {
* Load customize animation before animation start.
*/
boolean prepareNextAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo) {
- mCloseAnimation = mCustomAnimationLoader.load(
- animationInfo, false /* enterAnimation */);
- if (mCloseAnimation != null) {
- mEnterAnimation = mCustomAnimationLoader.load(
- animationInfo, true /* enterAnimation */);
+ final AnimationLoadResult result = mCustomAnimationLoader.loadAll(animationInfo);
+ if (result != null) {
+ mCloseAnimation = result.mCloseAnimation;
+ mEnterAnimation = result.mEnterAnimation;
+ mNextBackgroundColor = result.mBackgroundColor;
return true;
}
return false;
@@ -318,35 +326,79 @@ class CustomizeActivityAnimation {
}
}
+
+ static final class AnimationLoadResult {
+ Animation mCloseAnimation;
+ Animation mEnterAnimation;
+ int mBackgroundColor;
+ }
+
/**
* Helper class to load custom animation.
*/
static class CustomAnimationLoader {
- private final TransitionAnimation mTransitionAnimation;
+ final TransitionAnimation mTransitionAnimation;
CustomAnimationLoader(Context context) {
mTransitionAnimation = new TransitionAnimation(
context, false /* debug */, "CustomizeBackAnimation");
}
- Animation load(BackNavigationInfo.CustomAnimationInfo animationInfo,
- boolean enterAnimation) {
- final String packageName = animationInfo.getPackageName();
- if (packageName.isEmpty()) {
+ /**
+ * Load both enter and exit animation for the close activity transition.
+ * Note that the result is only valid if the exit animation has set and loaded success.
+ * If the entering animation has not set(i.e. 0), here will load the default entering
+ * animation for it.
+ *
+ * @param animationInfo The information of customize animation, which can be set from
+ * {@link Activity#overrideActivityTransition} and/or
+ * {@link LayoutParams#windowAnimations}
+ */
+ AnimationLoadResult loadAll(BackNavigationInfo.CustomAnimationInfo animationInfo) {
+ if (animationInfo.getPackageName().isEmpty()) {
return null;
}
- final int windowAnimations = animationInfo.getWindowAnimations();
- if (windowAnimations == 0) {
+ final Animation close = loadAnimation(animationInfo, false);
+ if (close == null) {
return null;
}
- final int attrs = enterAnimation
- ? com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation
- : com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation;
- Animation a = mTransitionAnimation.loadAnimationAttr(packageName, windowAnimations,
- attrs, false /* translucent */);
+ final Animation open = loadAnimation(animationInfo, true);
+ AnimationLoadResult result = new AnimationLoadResult();
+ result.mCloseAnimation = close;
+ result.mEnterAnimation = open;
+ result.mBackgroundColor = animationInfo.getCustomBackground();
+ return result;
+ }
+
+ /**
+ * Load enter or exit animation from CustomAnimationInfo
+ * @param animationInfo The information for customize animation.
+ * @param enterAnimation true when load for enter animation, false for exit animation.
+ * @return Loaded animation.
+ */
+ @Nullable
+ Animation loadAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo,
+ boolean enterAnimation) {
+ Animation a = null;
+ // Activity#overrideActivityTransition has higher priority than windowAnimations
+ // Try to get animation from Activity#overrideActivityTransition
+ if ((enterAnimation && animationInfo.getCustomEnterAnim() != 0)
+ || (!enterAnimation && animationInfo.getCustomExitAnim() != 0)) {
+ a = mTransitionAnimation.loadAppTransitionAnimation(
+ animationInfo.getPackageName(),
+ enterAnimation ? animationInfo.getCustomEnterAnim()
+ : animationInfo.getCustomExitAnim());
+ } else if (animationInfo.getWindowAnimations() != 0) {
+ // try to get animation from LayoutParams#windowAnimations
+ a = mTransitionAnimation.loadAnimationAttr(animationInfo.getPackageName(),
+ animationInfo.getWindowAnimations(), enterAnimation
+ ? R.styleable.WindowAnimation_activityCloseEnterAnimation
+ : R.styleable.WindowAnimation_activityCloseExitAnimation,
+ false /* translucent */);
+ }
// Only allow to load default animation for opening target.
if (a == null && enterAnimation) {
- a = mTransitionAnimation.loadDefaultAnimationAttr(attrs, false /* translucent */);
+ a = loadDefaultOpenAnimation();
}
if (a != null) {
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "custom animation loaded %s", a);
@@ -355,5 +407,11 @@ class CustomizeActivityAnimation {
}
return a;
}
+
+ private Animation loadDefaultOpenAnimation() {
+ return mTransitionAnimation.loadDefaultAnimationAttr(
+ R.styleable.WindowAnimation_activityCloseEnterAnimation,
+ false /* translucent */);
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java
index 2814ef9e26cc..e7d459893ce8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java
@@ -18,14 +18,20 @@ package com.android.wm.shell.back;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.app.WindowConfiguration;
+import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.RemoteException;
@@ -69,11 +75,7 @@ public class CustomizeActivityAnimationTest extends ShellTestCase {
mBackAnimationBackground, mock(SurfaceControl.Transaction.class),
mock(Choreographer.class));
spyOn(mCustomizeActivityAnimation);
- spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader);
- doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
- .load(any(), eq(false));
- doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
- .load(any(), eq(true));
+ spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation);
}
RemoteAnimationTarget createAnimationTarget(boolean open) {
@@ -87,6 +89,12 @@ public class CustomizeActivityAnimationTest extends ShellTestCase {
@Test
public void receiveFinishAfterInvoke() throws InterruptedException {
+ spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader);
+ doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
+ .loadAnimation(any(), eq(false));
+ doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
+ .loadAnimation(any(), eq(true));
+
mCustomizeActivityAnimation.prepareNextAnimation(
new BackNavigationInfo.CustomAnimationInfo("TestPackage"));
final RemoteAnimationTarget close = createAnimationTarget(false);
@@ -112,6 +120,12 @@ public class CustomizeActivityAnimationTest extends ShellTestCase {
@Test
public void receiveFinishAfterCancel() throws InterruptedException {
+ spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader);
+ doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
+ .loadAnimation(any(), eq(false));
+ doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
+ .loadAnimation(any(), eq(true));
+
mCustomizeActivityAnimation.prepareNextAnimation(
new BackNavigationInfo.CustomAnimationInfo("TestPackage"));
final RemoteAnimationTarget close = createAnimationTarget(false);
@@ -152,4 +166,67 @@ public class CustomizeActivityAnimationTest extends ShellTestCase {
verify(mCustomizeActivityAnimation).onGestureCommitted();
finishCalled.await(1, TimeUnit.SECONDS);
}
+
+ @Test
+ public void testLoadCustomAnimation() {
+ testLoadCustomAnimation(10, 20, 0);
+ }
+
+ @Test
+ public void testLoadCustomAnimationNoEnter() {
+ testLoadCustomAnimation(0, 10, 0);
+ }
+
+ @Test
+ public void testLoadWindowAnimations() {
+ testLoadCustomAnimation(0, 0, 30);
+ }
+
+ @Test
+ public void testCustomAnimationHigherThanWindowAnimations() {
+ testLoadCustomAnimation(10, 20, 30);
+ }
+
+ private void testLoadCustomAnimation(int enterResId, int exitResId, int windowAnimations) {
+ final String testPackage = "TestPackage";
+ BackNavigationInfo.Builder builder = new BackNavigationInfo.Builder()
+ .setCustomAnimation(testPackage, enterResId, exitResId, Color.GREEN)
+ .setWindowAnimations(testPackage, windowAnimations);
+ final BackNavigationInfo.CustomAnimationInfo info = builder.build()
+ .getCustomAnimationInfo();
+
+ doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader
+ .mTransitionAnimation)
+ .loadAppTransitionAnimation(eq(testPackage), eq(enterResId));
+ doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader
+ .mTransitionAnimation)
+ .loadAppTransitionAnimation(eq(testPackage), eq(exitResId));
+ doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader
+ .mTransitionAnimation)
+ .loadAnimationAttr(eq(testPackage), eq(windowAnimations), anyInt(), anyBoolean());
+ doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader
+ .mTransitionAnimation).loadDefaultAnimationAttr(anyInt(), anyBoolean());
+
+ CustomizeActivityAnimation.AnimationLoadResult result =
+ mCustomizeActivityAnimation.mCustomAnimationLoader.loadAll(info);
+
+ if (exitResId != 0) {
+ if (enterResId == 0) {
+ verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation,
+ never()).loadAppTransitionAnimation(eq(testPackage), eq(enterResId));
+ verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation)
+ .loadDefaultAnimationAttr(anyInt(), anyBoolean());
+ } else {
+ assertEquals(result.mEnterAnimation, mMockOpenAnimation);
+ }
+ assertEquals(result.mBackgroundColor, Color.GREEN);
+ assertEquals(result.mCloseAnimation, mMockCloseAnimation);
+ verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation, never())
+ .loadAnimationAttr(eq(testPackage), anyInt(), anyInt(), anyBoolean());
+ } else if (windowAnimations != 0) {
+ verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation,
+ times(2)).loadAnimationAttr(eq(testPackage), anyInt(), anyInt(), anyBoolean());
+ assertEquals(result.mCloseAnimation, mMockCloseAnimation);
+ }
+ }
}
diff --git a/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java b/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java
index f305fd35b7d9..e92157e7c867 100644
--- a/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java
+++ b/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java
@@ -47,7 +47,7 @@ import javax.tools.Diagnostic.Kind;
* Annotation processor for {@link SearchIndexable} that generates {@link SearchIndexableResources}
* subclasses.
*/
-@SupportedSourceVersion(SourceVersion.RELEASE_11)
+@SupportedSourceVersion(SourceVersion.RELEASE_17)
@SupportedAnnotationTypes({"com.android.settingslib.search.SearchIndexable"})
public class IndexableProcessor extends AbstractProcessor {
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index 7f24c52ccc6b..3d3535d2dbd2 100644
--- a/services/core/java/com/android/server/SystemServerInitThreadPool.java
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -25,7 +25,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.Preconditions;
-import com.android.server.am.ActivityManagerService;
+import com.android.server.am.StackTracesDumpHelper;
import com.android.server.utils.TimingsTraceAndSlog;
import java.io.PrintWriter;
@@ -188,12 +188,12 @@ public final class SystemServerInitThreadPool implements Dumpable {
}
/**
- * A helper function to call ActivityManagerService.dumpStackTraces().
+ * A helper function to call StackTracesDumpHelper.dumpStackTraces().
*/
private static void dumpStackTraces() {
final ArrayList<Integer> pids = new ArrayList<>();
pids.add(Process.myPid());
- ActivityManagerService.dumpStackTraces(pids,
+ StackTracesDumpHelper.dumpStackTraces(pids,
/* processCpuTracker= */null, /* lastPids= */null,
CompletableFuture.completedFuture(Watchdog.getInterestingNativePids()),
/* logExceptionCreatingFile= */null, /* subject= */null,
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 03821db3e43f..62651dd80cd9 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -56,6 +56,7 @@ import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.ZygoteConnectionConstants;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.am.ActivityManagerService;
+import com.android.server.am.StackTracesDumpHelper;
import com.android.server.am.TraceErrorLogger;
import com.android.server.criticalevents.CriticalEventLog;
import com.android.server.wm.SurfaceAnimationThread;
@@ -905,7 +906,7 @@ public class Watchdog implements Dumpable {
report.append(ResourcePressureUtil.currentPsiState());
ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(false);
StringWriter tracesFileException = new StringWriter();
- final File stack = ActivityManagerService.dumpStackTraces(
+ final File stack = StackTracesDumpHelper.dumpStackTraces(
pids, processCpuTracker, new SparseBooleanArray(),
CompletableFuture.completedFuture(getInterestingNativePids()), tracesFileException,
subject, criticalEvents, Runnable::run, /* latencyTracker= */null);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f2b6306aab5b..713b993824f9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -114,7 +114,6 @@ import static android.os.Process.setThreadScheduler;
import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
import static android.provider.Settings.Global.DEBUG_APP;
import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
-import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
import static android.view.Display.INVALID_DISPLAY;
@@ -123,7 +122,6 @@ import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_RE
import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NEW_MUTABLE_IMPLICIT_PENDING_INTENT_RETRIEVED;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALLOWLISTS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
@@ -372,7 +370,6 @@ import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -409,7 +406,6 @@ import com.android.internal.os.SomeArgs;
import com.android.internal.os.TimeoutRecord;
import com.android.internal.os.TransferPipe;
import com.android.internal.os.Zygote;
-import com.android.internal.os.anr.AnrLatencyTracker;
import com.android.internal.policy.AttributeCache;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ArrayUtils;
@@ -488,14 +484,10 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.nio.charset.StandardCharsets;
-import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
-import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -506,18 +498,13 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
-import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiFunction;
import java.util.function.Consumer;
-import java.util.function.Supplier;
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback, ActivityManagerGlobalLock {
@@ -555,8 +542,6 @@ public class ActivityManagerService extends IActivityManager.Stub
static final String SYSTEM_USER_HOME_NEEDED = "ro.system_user_home_needed";
- public static final String ANR_TRACE_DIR = "/data/anr";
-
// Maximum number of receivers an app can register.
private static final int MAX_RECEIVERS_ALLOWED_PER_APP = 1000;
@@ -618,10 +603,6 @@ public class ActivityManagerService extends IActivityManager.Stub
private static final int MAX_BUGREPORT_TITLE_SIZE = 100;
private static final int MAX_BUGREPORT_DESCRIPTION_SIZE = 150;
- private static final int NATIVE_DUMP_TIMEOUT_MS =
- 2000 * Build.HW_TIMEOUT_MULTIPLIER; // 2 seconds;
- private static final int JAVA_DUMP_MINIMUM_SIZE = 100; // 100 bytes.
-
OomAdjuster mOomAdjuster;
static final String EXTRA_TITLE = "android.intent.extra.TITLE";
@@ -3428,432 +3409,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- /**
- * If a stack trace dump file is configured, dump process stack traces.
- * @param firstPids of dalvik VM processes to dump stack traces for first
- * @param lastPids of dalvik VM processes to dump stack traces for last
- * @param nativePids optional list of native pids to dump stack crawls
- * @param logExceptionCreatingFile optional writer to which we log errors creating the file
- * @param auxiliaryTaskExecutor executor to execute auxiliary tasks on
- * @param latencyTracker the latency tracker instance of the current ANR.
- */
- public static File dumpStackTraces(ArrayList<Integer> firstPids,
- ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids,
- Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile,
- @NonNull Executor auxiliaryTaskExecutor, AnrLatencyTracker latencyTracker) {
- return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePidsFuture,
- logExceptionCreatingFile, null, null, null, auxiliaryTaskExecutor, latencyTracker);
- }
-
- /**
- * If a stack trace dump file is configured, dump process stack traces.
- * @param firstPids of dalvik VM processes to dump stack traces for first
- * @param lastPids of dalvik VM processes to dump stack traces for last
- * @param nativePids optional list of native pids to dump stack crawls
- * @param logExceptionCreatingFile optional writer to which we log errors creating the file
- * @param subject optional line related to the error
- * @param criticalEventSection optional lines containing recent critical events.
- * @param auxiliaryTaskExecutor executor to execute auxiliary tasks on
- * @param latencyTracker the latency tracker instance of the current ANR.
- */
- public static File dumpStackTraces(ArrayList<Integer> firstPids,
- ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids,
- Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile,
- String subject, String criticalEventSection, @NonNull Executor auxiliaryTaskExecutor,
- AnrLatencyTracker latencyTracker) {
- return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePidsFuture,
- logExceptionCreatingFile, null, subject, criticalEventSection,
- auxiliaryTaskExecutor, latencyTracker);
- }
-
- /**
- * @param firstPidEndOffset Optional, when it's set, it receives the start/end offset
- * of the very first pid to be dumped.
- */
- /* package */ static File dumpStackTraces(ArrayList<Integer> firstPids,
- ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids,
- Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile,
- AtomicLong firstPidEndOffset, String subject, String criticalEventSection,
- @NonNull Executor auxiliaryTaskExecutor, AnrLatencyTracker latencyTracker) {
- try {
-
- if (latencyTracker != null) {
- latencyTracker.dumpStackTracesStarted();
- }
-
- Slog.i(TAG, "dumpStackTraces pids=" + lastPids);
-
- // Measure CPU usage as soon as we're called in order to get a realistic sampling
- // of the top users at the time of the request.
- Supplier<ArrayList<Integer>> extraPidsSupplier = processCpuTracker != null
- ? () -> getExtraPids(processCpuTracker, lastPids, latencyTracker) : null;
- Future<ArrayList<Integer>> extraPidsFuture = null;
- if (extraPidsSupplier != null) {
- extraPidsFuture =
- CompletableFuture.supplyAsync(extraPidsSupplier, auxiliaryTaskExecutor);
- }
-
- final File tracesDir = new File(ANR_TRACE_DIR);
-
- // NOTE: We should consider creating the file in native code atomically once we've
- // gotten rid of the old scheme of dumping and lot of the code that deals with paths
- // can be removed.
- File tracesFile;
- try {
- tracesFile = createAnrDumpFile(tracesDir);
- } catch (IOException e) {
- Slog.w(TAG, "Exception creating ANR dump file:", e);
- if (logExceptionCreatingFile != null) {
- logExceptionCreatingFile.append(
- "----- Exception creating ANR dump file -----\n");
- e.printStackTrace(new PrintWriter(logExceptionCreatingFile));
- }
- if (latencyTracker != null) {
- latencyTracker.anrSkippedDumpStackTraces();
- }
- return null;
- }
-
- if (subject != null || criticalEventSection != null) {
- appendtoANRFile(tracesFile.getAbsolutePath(),
- (subject != null ? "Subject: " + subject + "\n\n" : "")
- + (criticalEventSection != null ? criticalEventSection : ""));
- }
-
- long firstPidEndPos = dumpStackTraces(
- tracesFile.getAbsolutePath(), firstPids, nativePidsFuture,
- extraPidsFuture, latencyTracker);
- if (firstPidEndOffset != null) {
- firstPidEndOffset.set(firstPidEndPos);
- }
- // Each set of ANR traces is written to a separate file and dumpstate will process
- // all such files and add them to a captured bug report if they're recent enough.
- maybePruneOldTraces(tracesDir);
-
- return tracesFile;
- } finally {
- if (latencyTracker != null) {
- latencyTracker.dumpStackTracesEnded();
- }
- }
-
- }
-
- @GuardedBy("ActivityManagerService.class")
- private static SimpleDateFormat sAnrFileDateFormat;
- static final String ANR_FILE_PREFIX = "anr_";
-
- private static ArrayList<Integer> getExtraPids(ProcessCpuTracker processCpuTracker,
- SparseBooleanArray lastPids, AnrLatencyTracker latencyTracker) {
- if (latencyTracker != null) {
- latencyTracker.processCpuTrackerMethodsCalled();
- }
- ArrayList<Integer> extraPids = new ArrayList<>();
- processCpuTracker.init();
- try {
- Thread.sleep(200);
- } catch (InterruptedException ignored) {
- }
-
- processCpuTracker.update();
-
- // We'll take the stack crawls of just the top apps using CPU.
- final int workingStatsNumber = processCpuTracker.countWorkingStats();
- for (int i = 0; i < workingStatsNumber && extraPids.size() < 2; i++) {
- ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i);
- if (lastPids.indexOfKey(stats.pid) >= 0) {
- if (DEBUG_ANR) {
- Slog.d(TAG, "Collecting stacks for extra pid " + stats.pid);
- }
-
- extraPids.add(stats.pid);
- } else {
- Slog.i(TAG,
- "Skipping next CPU consuming process, not a java proc: "
- + stats.pid);
- }
- }
- if (latencyTracker != null) {
- latencyTracker.processCpuTrackerMethodsReturned();
- }
- return extraPids;
- }
-
- private static synchronized File createAnrDumpFile(File tracesDir) throws IOException {
- if (sAnrFileDateFormat == null) {
- sAnrFileDateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS");
- }
-
- final String formattedDate = sAnrFileDateFormat.format(new Date());
- final File anrFile = new File(tracesDir, ANR_FILE_PREFIX + formattedDate);
-
- if (anrFile.createNewFile()) {
- FileUtils.setPermissions(anrFile.getAbsolutePath(), 0600, -1, -1); // -rw-------
- return anrFile;
- } else {
- throw new IOException("Unable to create ANR dump file: createNewFile failed");
- }
- }
-
- /**
- * Prune all trace files that are more than a day old.
- *
- * NOTE: It might make sense to move this functionality to tombstoned eventually, along with a
- * shift away from anr_XX and tombstone_XX to a more descriptive name. We do it here for now
- * since it's the system_server that creates trace files for most ANRs.
- */
- private static void maybePruneOldTraces(File tracesDir) {
- final File[] files = tracesDir.listFiles();
- if (files == null) return;
-
- final int max = SystemProperties.getInt("tombstoned.max_anr_count", 64);
- final long now = System.currentTimeMillis();
- try {
- Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
- for (int i = 0; i < files.length; ++i) {
- if (i > max || (now - files[i].lastModified()) > DAY_IN_MILLIS) {
- if (!files[i].delete()) {
- Slog.w(TAG, "Unable to prune stale trace file: " + files[i]);
- }
- }
- }
- } catch (IllegalArgumentException e) {
- // The modification times changed while we were sorting. Bail...
- // https://issuetracker.google.com/169836837
- Slog.w(TAG, "tombstone modification times changed while sorting; not pruning", e);
- }
- }
-
- /**
- * Dump java traces for process {@code pid} to the specified file. If java trace dumping
- * fails, a native backtrace is attempted. Note that the timeout {@code timeoutMs} only applies
- * to the java section of the trace, a further {@code NATIVE_DUMP_TIMEOUT_MS} might be spent
- * attempting to obtain native traces in the case of a failure. Returns the total time spent
- * capturing traces.
- */
- private static long dumpJavaTracesTombstoned(int pid, String fileName, long timeoutMs) {
- final long timeStart = SystemClock.elapsedRealtime();
- int headerSize = writeUptimeStartHeaderForPid(pid, fileName);
- boolean javaSuccess = Debug.dumpJavaBacktraceToFileTimeout(pid, fileName,
- (int) (timeoutMs / 1000));
- if (javaSuccess) {
- // Check that something is in the file, actually. Try-catch should not be necessary,
- // but better safe than sorry.
- try {
- long size = new File(fileName).length();
- if ((size - headerSize) < JAVA_DUMP_MINIMUM_SIZE) {
- Slog.w(TAG, "Successfully created Java ANR file is empty!");
- javaSuccess = false;
- }
- } catch (Exception e) {
- Slog.w(TAG, "Unable to get ANR file size", e);
- javaSuccess = false;
- }
- }
- if (!javaSuccess) {
- Slog.w(TAG, "Dumping Java threads failed, initiating native stack dump.");
- if (!Debug.dumpNativeBacktraceToFileTimeout(pid, fileName,
- (NATIVE_DUMP_TIMEOUT_MS / 1000))) {
- Slog.w(TAG, "Native stack dump failed!");
- }
- }
-
- return SystemClock.elapsedRealtime() - timeStart;
- }
-
- private static int appendtoANRFile(String fileName, String text) {
- try (FileOutputStream fos = new FileOutputStream(fileName, true)) {
- byte[] header = text.getBytes(StandardCharsets.UTF_8);
- fos.write(header);
- return header.length;
- } catch (IOException e) {
- Slog.w(TAG, "Exception writing to ANR dump file:", e);
- return 0;
- }
- }
-
- /*
- * Writes a header containing the process id and the current system uptime.
- */
- private static int writeUptimeStartHeaderForPid(int pid, String fileName) {
- return appendtoANRFile(fileName, "----- dumping pid: " + pid + " at "
- + SystemClock.uptimeMillis() + "\n");
- }
-
-
- /**
- * @return The end offset of the trace of the very first PID
- */
- public static long dumpStackTraces(String tracesFile,
- ArrayList<Integer> firstPids, Future<ArrayList<Integer>> nativePidsFuture,
- Future<ArrayList<Integer>> extraPidsFuture, AnrLatencyTracker latencyTracker) {
-
- Slog.i(TAG, "Dumping to " + tracesFile);
-
- // We don't need any sort of inotify based monitoring when we're dumping traces via
- // tombstoned. Data is piped to an "intercept" FD installed in tombstoned so we're in full
- // control of all writes to the file in question.
-
- // We must complete all stack dumps within 20 seconds.
- long remainingTime = 20 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
-
- // As applications are usually interested with the ANR stack traces, but we can't share with
- // them the stack traces other than their own stacks. So after the very first PID is
- // dumped, remember the current file size.
- long firstPidEnd = -1;
-
- // First collect all of the stacks of the most important pids.
- if (firstPids != null) {
- if (latencyTracker != null) {
- latencyTracker.dumpingFirstPidsStarted();
- }
-
- int num = firstPids.size();
- for (int i = 0; i < num; i++) {
- final int pid = firstPids.get(i);
- // We don't copy ANR traces from the system_server intentionally.
- final boolean firstPid = i == 0 && MY_PID != pid;
- if (latencyTracker != null) {
- latencyTracker.dumpingPidStarted(pid);
- }
-
- Slog.i(TAG, "Collecting stacks for pid " + pid);
- final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile,
- remainingTime);
- if (latencyTracker != null) {
- latencyTracker.dumpingPidEnded();
- }
-
- remainingTime -= timeTaken;
- if (remainingTime <= 0) {
- Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + pid
- + "); deadline exceeded.");
- return firstPidEnd;
- }
-
- if (firstPid) {
- firstPidEnd = new File(tracesFile).length();
- // Full latency dump
- if (latencyTracker != null) {
- appendtoANRFile(tracesFile,
- latencyTracker.dumpAsCommaSeparatedArrayWithHeader());
- }
- }
- if (DEBUG_ANR) {
- Slog.d(TAG, "Done with pid " + firstPids.get(i) + " in " + timeTaken + "ms");
- }
- }
- if (latencyTracker != null) {
- latencyTracker.dumpingFirstPidsEnded();
- }
- }
-
- // Next collect the stacks of the native pids
- ArrayList<Integer> nativePids = collectPids(nativePidsFuture, "native pids");
-
- Slog.i(TAG, "dumpStackTraces nativepids=" + nativePids);
-
- if (nativePids != null) {
- if (latencyTracker != null) {
- latencyTracker.dumpingNativePidsStarted();
- }
- for (int pid : nativePids) {
- Slog.i(TAG, "Collecting stacks for native pid " + pid);
- final long nativeDumpTimeoutMs = Math.min(NATIVE_DUMP_TIMEOUT_MS, remainingTime);
-
- if (latencyTracker != null) {
- latencyTracker.dumpingPidStarted(pid);
- }
- final long start = SystemClock.elapsedRealtime();
- Debug.dumpNativeBacktraceToFileTimeout(
- pid, tracesFile, (int) (nativeDumpTimeoutMs / 1000));
- final long timeTaken = SystemClock.elapsedRealtime() - start;
- if (latencyTracker != null) {
- latencyTracker.dumpingPidEnded();
- }
- remainingTime -= timeTaken;
- if (remainingTime <= 0) {
- Slog.e(TAG, "Aborting stack trace dump (current native pid=" + pid +
- "); deadline exceeded.");
- return firstPidEnd;
- }
-
- if (DEBUG_ANR) {
- Slog.d(TAG, "Done with native pid " + pid + " in " + timeTaken + "ms");
- }
- }
- if (latencyTracker != null) {
- latencyTracker.dumpingNativePidsEnded();
- }
- }
-
- // Lastly, dump stacks for all extra PIDs from the CPU tracker.
- ArrayList<Integer> extraPids = collectPids(extraPidsFuture, "extra pids");
-
- if (extraPidsFuture != null) {
- try {
- extraPids = extraPidsFuture.get();
- } catch (ExecutionException e) {
- Slog.w(TAG, "Failed to collect extra pids", e.getCause());
- } catch (InterruptedException e) {
- Slog.w(TAG, "Interrupted while collecting extra pids", e);
- }
- }
- Slog.i(TAG, "dumpStackTraces extraPids=" + extraPids);
-
- if (extraPids != null) {
- if (latencyTracker != null) {
- latencyTracker.dumpingExtraPidsStarted();
- }
- for (int pid : extraPids) {
- Slog.i(TAG, "Collecting stacks for extra pid " + pid);
- if (latencyTracker != null) {
- latencyTracker.dumpingPidStarted(pid);
- }
- final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime);
- if (latencyTracker != null) {
- latencyTracker.dumpingPidEnded();
- }
- remainingTime -= timeTaken;
- if (remainingTime <= 0) {
- Slog.e(TAG, "Aborting stack trace dump (current extra pid=" + pid +
- "); deadline exceeded.");
- return firstPidEnd;
- }
-
- if (DEBUG_ANR) {
- Slog.d(TAG, "Done with extra pid " + pid + " in " + timeTaken + "ms");
- }
- }
- if (latencyTracker != null) {
- latencyTracker.dumpingExtraPidsEnded();
- }
- }
- // Append the dumping footer with the current uptime
- appendtoANRFile(tracesFile, "----- dumping ended at " + SystemClock.uptimeMillis() + "\n");
- Slog.i(TAG, "Done dumping");
-
- return firstPidEnd;
- }
-
- private static ArrayList<Integer> collectPids(Future<ArrayList<Integer>> pidsFuture,
- String logName) {
-
- ArrayList<Integer> pids = null;
-
- if (pidsFuture == null) {
- return pids;
- }
- try {
- pids = pidsFuture.get();
- } catch (ExecutionException e) {
- Slog.w(TAG, "Failed to collect " + logName, e.getCause());
- } catch (InterruptedException e) {
- Slog.w(TAG, "Interrupted while collecting " + logName , e);
- }
- return pids;
- }
-
@Override
public boolean clearApplicationUserData(final String packageName, boolean keepState,
final IPackageDataObserver observer, int userId) {
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 1ba326680fc2..44436369fb31 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -1153,7 +1153,7 @@ public final class AppExitInfoTracker {
final ArraySet<String> allFiles = new ArraySet();
final File[] files = mProcExitStoreDir.listFiles((f) -> {
final String name = f.getName();
- boolean trace = name.startsWith(ActivityManagerService.ANR_FILE_PREFIX)
+ boolean trace = name.startsWith(StackTracesDumpHelper.ANR_FILE_PREFIX)
&& name.endsWith(APP_TRACE_FILE_SUFFIX);
if (trace) {
allFiles.add(name);
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 2517c9c1ea4b..1d48cb25f03a 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -493,7 +493,7 @@ class ProcessErrorStateRecord {
StringWriter tracesFileException = new StringWriter();
// To hold the start and end offset to the ANR trace file respectively.
final AtomicLong firstPidEndOffset = new AtomicLong(-1);
- File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
+ File tracesFile = StackTracesDumpHelper.dumpStackTraces(firstPids,
isSilentAnr ? null : processCpuTracker, isSilentAnr ? null : lastPids,
nativePidsFuture, tracesFileException, firstPidEndOffset, annotation,
criticalEventLog, auxiliaryTaskExecutor, latencyTracker);
diff --git a/services/core/java/com/android/server/am/StackTracesDumpHelper.java b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
new file mode 100644
index 000000000000..937332894dbd
--- /dev/null
+++ b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
@@ -0,0 +1,483 @@
+/*
+ * 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.am;
+
+import static android.text.format.DateUtils.DAY_IN_MILLIS;
+
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.annotation.NonNull;
+import android.os.Build;
+import android.os.Debug;
+import android.os.FileUtils;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.os.anr.AnrLatencyTracker;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+
+
+/**
+ * A helper for dumping stack traces.
+ */
+public class StackTracesDumpHelper {
+ static final String TAG = TAG_WITH_CLASS_NAME ? "StackTracesDumpHelper" : TAG_AM;
+
+ @GuardedBy("StackTracesDumpHelper.class")
+ private static final SimpleDateFormat ANR_FILE_DATE_FORMAT =
+ new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS");
+
+ static final String ANR_FILE_PREFIX = "anr_";
+ public static final String ANR_TRACE_DIR = "/data/anr";
+
+ private static final int NATIVE_DUMP_TIMEOUT_MS =
+ 2000 * Build.HW_TIMEOUT_MULTIPLIER; // 2 seconds;
+ private static final int JAVA_DUMP_MINIMUM_SIZE = 100; // 100 bytes.
+
+ /**
+ * If a stack trace dump file is configured, dump process stack traces.
+ * @param firstPids of dalvik VM processes to dump stack traces for first
+ * @param lastPids of dalvik VM processes to dump stack traces for last
+ * @param nativePidsFuture optional future for a list of native pids to dump stack crawls
+ * @param logExceptionCreatingFile optional writer to which we log errors creating the file
+ * @param auxiliaryTaskExecutor executor to execute auxiliary tasks on
+ * @param latencyTracker the latency tracker instance of the current ANR.
+ */
+ public static File dumpStackTraces(ArrayList<Integer> firstPids,
+ ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids,
+ Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile,
+ @NonNull Executor auxiliaryTaskExecutor, AnrLatencyTracker latencyTracker) {
+ return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePidsFuture,
+ logExceptionCreatingFile, null, null, null, auxiliaryTaskExecutor, latencyTracker);
+ }
+
+ /**
+ * @param subject the subject of the dumped traces
+ * @param criticalEventSection the critical event log, passed as a string
+ */
+ public static File dumpStackTraces(ArrayList<Integer> firstPids,
+ ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids,
+ Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile,
+ String subject, String criticalEventSection, @NonNull Executor auxiliaryTaskExecutor,
+ AnrLatencyTracker latencyTracker) {
+ return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePidsFuture,
+ logExceptionCreatingFile, null, subject, criticalEventSection,
+ auxiliaryTaskExecutor, latencyTracker);
+ }
+
+ /**
+ * @param firstPidEndOffset Optional, when it's set, it receives the start/end offset
+ * of the very first pid to be dumped.
+ */
+ /* package */ static File dumpStackTraces(ArrayList<Integer> firstPids,
+ ProcessCpuTracker processCpuTracker, SparseBooleanArray lastPids,
+ Future<ArrayList<Integer>> nativePidsFuture, StringWriter logExceptionCreatingFile,
+ AtomicLong firstPidEndOffset, String subject, String criticalEventSection,
+ @NonNull Executor auxiliaryTaskExecutor, AnrLatencyTracker latencyTracker) {
+ try {
+
+ if (latencyTracker != null) {
+ latencyTracker.dumpStackTracesStarted();
+ }
+
+ Slog.i(TAG, "dumpStackTraces pids=" + lastPids);
+
+ // Measure CPU usage as soon as we're called in order to get a realistic sampling
+ // of the top users at the time of the request.
+ Supplier<ArrayList<Integer>> extraPidsSupplier = processCpuTracker != null
+ ? () -> getExtraPids(processCpuTracker, lastPids, latencyTracker) : null;
+ Future<ArrayList<Integer>> extraPidsFuture = null;
+ if (extraPidsSupplier != null) {
+ extraPidsFuture =
+ CompletableFuture.supplyAsync(extraPidsSupplier, auxiliaryTaskExecutor);
+ }
+
+ final File tracesDir = new File(ANR_TRACE_DIR);
+
+ // NOTE: We should consider creating the file in native code atomically once we've
+ // gotten rid of the old scheme of dumping and lot of the code that deals with paths
+ // can be removed.
+ File tracesFile;
+ try {
+ tracesFile = createAnrDumpFile(tracesDir);
+ } catch (IOException e) {
+ Slog.w(TAG, "Exception creating ANR dump file:", e);
+ if (logExceptionCreatingFile != null) {
+ logExceptionCreatingFile.append(
+ "----- Exception creating ANR dump file -----\n");
+ e.printStackTrace(new PrintWriter(logExceptionCreatingFile));
+ }
+ if (latencyTracker != null) {
+ latencyTracker.anrSkippedDumpStackTraces();
+ }
+ return null;
+ }
+
+ if (subject != null || criticalEventSection != null) {
+ appendtoANRFile(tracesFile.getAbsolutePath(),
+ (subject != null ? "Subject: " + subject + "\n\n" : "")
+ + (criticalEventSection != null ? criticalEventSection : ""));
+ }
+
+ long firstPidEndPos = dumpStackTraces(
+ tracesFile.getAbsolutePath(), firstPids, nativePidsFuture,
+ extraPidsFuture, latencyTracker);
+ if (firstPidEndOffset != null) {
+ firstPidEndOffset.set(firstPidEndPos);
+ }
+ // Each set of ANR traces is written to a separate file and dumpstate will process
+ // all such files and add them to a captured bug report if they're recent enough.
+ maybePruneOldTraces(tracesDir);
+
+ return tracesFile;
+ } finally {
+ if (latencyTracker != null) {
+ latencyTracker.dumpStackTracesEnded();
+ }
+ }
+
+ }
+
+ /**
+ * @return The end offset of the trace of the very first PID
+ */
+ public static long dumpStackTraces(String tracesFile,
+ ArrayList<Integer> firstPids, Future<ArrayList<Integer>> nativePidsFuture,
+ Future<ArrayList<Integer>> extraPidsFuture, AnrLatencyTracker latencyTracker) {
+
+ Slog.i(TAG, "Dumping to " + tracesFile);
+
+ // We don't need any sort of inotify based monitoring when we're dumping traces via
+ // tombstoned. Data is piped to an "intercept" FD installed in tombstoned so we're in full
+ // control of all writes to the file in question.
+
+ // We must complete all stack dumps within 20 seconds.
+ long remainingTime = 20 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
+
+ // As applications are usually interested with the ANR stack traces, but we can't share with
+ // them the stack traces other than their own stacks. So after the very first PID is
+ // dumped, remember the current file size.
+ long firstPidEnd = -1;
+
+ // First collect all of the stacks of the most important pids.
+ if (firstPids != null) {
+ if (latencyTracker != null) {
+ latencyTracker.dumpingFirstPidsStarted();
+ }
+
+ int num = firstPids.size();
+ for (int i = 0; i < num; i++) {
+ final int pid = firstPids.get(i);
+ // We don't copy ANR traces from the system_server intentionally.
+ final boolean firstPid = i == 0 && ActivityManagerService.MY_PID != pid;
+ if (latencyTracker != null) {
+ latencyTracker.dumpingPidStarted(pid);
+ }
+
+ Slog.i(TAG, "Collecting stacks for pid " + pid);
+ final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile,
+ remainingTime);
+ if (latencyTracker != null) {
+ latencyTracker.dumpingPidEnded();
+ }
+
+ remainingTime -= timeTaken;
+ if (remainingTime <= 0) {
+ Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + pid
+ + "); deadline exceeded.");
+ return firstPidEnd;
+ }
+
+ if (firstPid) {
+ firstPidEnd = new File(tracesFile).length();
+ // Full latency dump
+ if (latencyTracker != null) {
+ appendtoANRFile(tracesFile,
+ latencyTracker.dumpAsCommaSeparatedArrayWithHeader());
+ }
+ }
+ if (DEBUG_ANR) {
+ Slog.d(TAG, "Done with pid " + firstPids.get(i) + " in " + timeTaken + "ms");
+ }
+ }
+ if (latencyTracker != null) {
+ latencyTracker.dumpingFirstPidsEnded();
+ }
+ }
+
+ // Next collect the stacks of the native pids
+ ArrayList<Integer> nativePids = collectPids(nativePidsFuture, "native pids");
+
+ Slog.i(TAG, "dumpStackTraces nativepids=" + nativePids);
+
+ if (nativePids != null) {
+ if (latencyTracker != null) {
+ latencyTracker.dumpingNativePidsStarted();
+ }
+ for (int pid : nativePids) {
+ Slog.i(TAG, "Collecting stacks for native pid " + pid);
+ final long nativeDumpTimeoutMs = Math.min(NATIVE_DUMP_TIMEOUT_MS, remainingTime);
+
+ if (latencyTracker != null) {
+ latencyTracker.dumpingPidStarted(pid);
+ }
+ final long start = SystemClock.elapsedRealtime();
+ Debug.dumpNativeBacktraceToFileTimeout(
+ pid, tracesFile, (int) (nativeDumpTimeoutMs / 1000));
+ final long timeTaken = SystemClock.elapsedRealtime() - start;
+ if (latencyTracker != null) {
+ latencyTracker.dumpingPidEnded();
+ }
+ remainingTime -= timeTaken;
+ if (remainingTime <= 0) {
+ Slog.e(TAG, "Aborting stack trace dump (current native pid=" + pid
+ + "); deadline exceeded.");
+ return firstPidEnd;
+ }
+
+ if (DEBUG_ANR) {
+ Slog.d(TAG, "Done with native pid " + pid + " in " + timeTaken + "ms");
+ }
+ }
+ if (latencyTracker != null) {
+ latencyTracker.dumpingNativePidsEnded();
+ }
+ }
+
+ // Lastly, dump stacks for all extra PIDs from the CPU tracker.
+ ArrayList<Integer> extraPids = collectPids(extraPidsFuture, "extra pids");
+
+ if (extraPidsFuture != null) {
+ try {
+ extraPids = extraPidsFuture.get();
+ } catch (ExecutionException e) {
+ Slog.w(TAG, "Failed to collect extra pids", e.getCause());
+ } catch (InterruptedException e) {
+ Slog.w(TAG, "Interrupted while collecting extra pids", e);
+ }
+ }
+ Slog.i(TAG, "dumpStackTraces extraPids=" + extraPids);
+
+ if (extraPids != null) {
+ if (latencyTracker != null) {
+ latencyTracker.dumpingExtraPidsStarted();
+ }
+ for (int pid : extraPids) {
+ Slog.i(TAG, "Collecting stacks for extra pid " + pid);
+ if (latencyTracker != null) {
+ latencyTracker.dumpingPidStarted(pid);
+ }
+ final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime);
+ if (latencyTracker != null) {
+ latencyTracker.dumpingPidEnded();
+ }
+ remainingTime -= timeTaken;
+ if (remainingTime <= 0) {
+ Slog.e(TAG, "Aborting stack trace dump (current extra pid=" + pid
+ + "); deadline exceeded.");
+ return firstPidEnd;
+ }
+
+ if (DEBUG_ANR) {
+ Slog.d(TAG, "Done with extra pid " + pid + " in " + timeTaken + "ms");
+ }
+ }
+ if (latencyTracker != null) {
+ latencyTracker.dumpingExtraPidsEnded();
+ }
+ }
+ // Append the dumping footer with the current uptime
+ appendtoANRFile(tracesFile, "----- dumping ended at " + SystemClock.uptimeMillis() + "\n");
+ Slog.i(TAG, "Done dumping");
+
+ return firstPidEnd;
+ }
+
+ private static synchronized File createAnrDumpFile(File tracesDir) throws IOException {
+ final String formattedDate = ANR_FILE_DATE_FORMAT.format(new Date());
+ final File anrFile = new File(tracesDir, ANR_FILE_PREFIX + formattedDate);
+
+ if (anrFile.createNewFile()) {
+ FileUtils.setPermissions(anrFile.getAbsolutePath(), 0600, -1, -1); // -rw-------
+ return anrFile;
+ } else {
+ throw new IOException("Unable to create ANR dump file: createNewFile failed");
+ }
+ }
+
+ private static ArrayList<Integer> getExtraPids(ProcessCpuTracker processCpuTracker,
+ SparseBooleanArray lastPids, AnrLatencyTracker latencyTracker) {
+ if (latencyTracker != null) {
+ latencyTracker.processCpuTrackerMethodsCalled();
+ }
+ ArrayList<Integer> extraPids = new ArrayList<>();
+ processCpuTracker.init();
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException ignored) {
+ }
+
+ processCpuTracker.update();
+
+ // We'll take the stack crawls of just the top apps using CPU.
+ final int workingStatsNumber = processCpuTracker.countWorkingStats();
+ for (int i = 0; i < workingStatsNumber && extraPids.size() < 2; i++) {
+ ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i);
+ if (lastPids.indexOfKey(stats.pid) >= 0) {
+ if (DEBUG_ANR) {
+ Slog.d(TAG, "Collecting stacks for extra pid " + stats.pid);
+ }
+
+ extraPids.add(stats.pid);
+ } else {
+ Slog.i(TAG,
+ "Skipping next CPU consuming process, not a java proc: "
+ + stats.pid);
+ }
+ }
+ if (latencyTracker != null) {
+ latencyTracker.processCpuTrackerMethodsReturned();
+ }
+ return extraPids;
+ }
+
+ /**
+ * Prune all trace files that are more than a day old.
+ *
+ * NOTE: It might make sense to move this functionality to tombstoned eventually, along with a
+ * shift away from anr_XX and tombstone_XX to a more descriptive name. We do it here for now
+ * since it's the system_server that creates trace files for most ANRs.
+ */
+ private static void maybePruneOldTraces(File tracesDir) {
+ final File[] files = tracesDir.listFiles();
+ if (files == null) return;
+
+ final int max = SystemProperties.getInt("tombstoned.max_anr_count", 64);
+ final long now = System.currentTimeMillis();
+ try {
+ Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
+ for (int i = 0; i < files.length; ++i) {
+ if (i > max || (now - files[i].lastModified()) > DAY_IN_MILLIS) {
+ if (!files[i].delete()) {
+ Slog.w(TAG, "Unable to prune stale trace file: " + files[i]);
+ }
+ }
+ }
+ } catch (IllegalArgumentException e) {
+ // The modification times changed while we were sorting. Bail...
+ // https://issuetracker.google.com/169836837
+ Slog.w(TAG, "tombstone modification times changed while sorting; not pruning", e);
+ }
+ }
+ /**
+ * Dump java traces for process {@code pid} to the specified file. If java trace dumping
+ * fails, a native backtrace is attempted. Note that the timeout {@code timeoutMs} only applies
+ * to the java section of the trace, a further {@code NATIVE_DUMP_TIMEOUT_MS} might be spent
+ * attempting to obtain native traces in the case of a failure. Returns the total time spent
+ * capturing traces.
+ */
+ private static long dumpJavaTracesTombstoned(int pid, String fileName, long timeoutMs) {
+ final long timeStart = SystemClock.elapsedRealtime();
+ int headerSize = writeUptimeStartHeaderForPid(pid, fileName);
+ boolean javaSuccess = Debug.dumpJavaBacktraceToFileTimeout(pid, fileName,
+ (int) (timeoutMs / 1000));
+ if (javaSuccess) {
+ // Check that something is in the file, actually. Try-catch should not be necessary,
+ // but better safe than sorry.
+ try {
+ long size = new File(fileName).length();
+ if ((size - headerSize) < JAVA_DUMP_MINIMUM_SIZE) {
+ Slog.w(TAG, "Successfully created Java ANR file is empty!");
+ javaSuccess = false;
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Unable to get ANR file size", e);
+ javaSuccess = false;
+ }
+ }
+ if (!javaSuccess) {
+ Slog.w(TAG, "Dumping Java threads failed, initiating native stack dump.");
+ if (!Debug.dumpNativeBacktraceToFileTimeout(pid, fileName,
+ (NATIVE_DUMP_TIMEOUT_MS / 1000))) {
+ Slog.w(TAG, "Native stack dump failed!");
+ }
+ }
+
+ return SystemClock.elapsedRealtime() - timeStart;
+ }
+
+ private static int appendtoANRFile(String fileName, String text) {
+ try (FileOutputStream fos = new FileOutputStream(fileName, true)) {
+ byte[] header = text.getBytes(StandardCharsets.UTF_8);
+ fos.write(header);
+ return header.length;
+ } catch (IOException e) {
+ Slog.w(TAG, "Exception writing to ANR dump file:", e);
+ return 0;
+ }
+ }
+
+ /*
+ * Writes a header containing the process id and the current system uptime.
+ */
+ private static int writeUptimeStartHeaderForPid(int pid, String fileName) {
+ return appendtoANRFile(fileName, "----- dumping pid: " + pid + " at "
+ + SystemClock.uptimeMillis() + "\n");
+ }
+
+ private static ArrayList<Integer> collectPids(Future<ArrayList<Integer>> pidsFuture,
+ String logName) {
+
+ ArrayList<Integer> pids = null;
+
+ if (pidsFuture == null) {
+ return pids;
+ }
+ try {
+ pids = pidsFuture.get();
+ } catch (ExecutionException e) {
+ Slog.w(TAG, "Failed to collect " + logName, e.getCause());
+ } catch (InterruptedException e) {
+ Slog.w(TAG, "Interrupted while collecting " + logName , e);
+ }
+ return pids;
+ }
+
+}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index ad5ae5f4d0d9..745e40411bad 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -4600,7 +4600,8 @@ public final class DisplayManagerService extends SystemService {
public void onDesiredDisplayModeSpecsChanged() {
synchronized (mSyncRoot) {
mChanged = false;
- mLogicalDisplayMapper.forEachLocked(mSpecsChangedConsumer);
+ mLogicalDisplayMapper.forEachLocked(mSpecsChangedConsumer,
+ /* includeDisabled= */ false);
if (mChanged) {
scheduleTraversalLocked(false);
mChanged = false;
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 250ba4350f1d..424eedc876ec 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -302,9 +302,16 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
}
public void forEachLocked(Consumer<LogicalDisplay> consumer) {
+ forEachLocked(consumer, /* includeDisabled= */ true);
+ }
+
+ public void forEachLocked(Consumer<LogicalDisplay> consumer, boolean includeDisabled) {
final int count = mLogicalDisplays.size();
for (int i = 0; i < count; i++) {
- consumer.accept(mLogicalDisplays.valueAt(i));
+ LogicalDisplay display = mLogicalDisplays.valueAt(i);
+ if (display.isEnabledLocked() || includeDisabled) {
+ consumer.accept(display);
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index b4b8cb2a370d..ad77ef7ca975 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -28,6 +28,7 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Intent;
@@ -620,12 +621,15 @@ public final class SuspendPackageHelper {
extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
final int flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND;
+ final Bundle options = new BroadcastOptions()
+ .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
+ .toBundle();
handler.post(() -> mBroadcastHelper.sendPackageBroadcast(intent, null /* pkg */,
extras, flags, null /* targetPkg */, null /* finishedReceiver */,
new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */,
(callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList(
mPm.snapshotComputer(), callingUid, intentExtras),
- null /* bOptions */));
+ options));
}
/**
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a36e9f961211..927a722defac 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1276,7 +1276,15 @@ public class UserManagerService extends IUserManager.Stub {
getDevicePolicyManagerInternal().broadcastIntentToManifestReceivers(
intent, parentHandle, /* requiresPermission= */ true);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
- mContext.sendBroadcastAsUser(intent, parentHandle);
+ final Bundle options = new BroadcastOptions()
+ .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
+ .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
+ // Both actions use single namespace because only the final state matters.
+ .setDeliveryGroupMatchingKey(
+ Intent.ACTION_MANAGED_PROFILE_AVAILABLE /* namespace */,
+ String.valueOf(profileHandle.getIdentifier()) /* key */)
+ .toBundle();
+ mContext.sendBroadcastAsUser(intent, parentHandle, /* receiverPermission= */ null, options);
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 555cd38806e6..7b9cc6fee602 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -75,9 +75,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IMMERSIVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerService.ANR_TRACE_DIR;
import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
-import static com.android.server.am.ActivityManagerService.dumpStackTraces;
import static com.android.server.am.ActivityManagerServiceDumpActivitiesProto.ROOT_WINDOW_CONTAINER;
import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.CONFIG_WILL_CHANGE;
import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.CONTROLLER;
@@ -95,6 +93,8 @@ import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.Scr
import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.PACKAGE;
import static com.android.server.am.EventLogTags.writeBootProgressEnableScreen;
import static com.android.server.am.EventLogTags.writeConfigurationChanged;
+import static com.android.server.am.StackTracesDumpHelper.ANR_TRACE_DIR;
+import static com.android.server.am.StackTracesDumpHelper.dumpStackTraces;
import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_FIRST_ORDERED_ID;
import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_LAST_ORDERED_ID;
import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_FIRST_ORDERED_ID;
diff --git a/services/core/java/com/android/server/wm/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java
index bbe7a33669c8..90ec964e2f0f 100644
--- a/services/core/java/com/android/server/wm/AnrController.java
+++ b/services/core/java/com/android/server/wm/AnrController.java
@@ -34,7 +34,7 @@ import android.util.SparseArray;
import android.view.InputApplicationHandle;
import com.android.internal.os.TimeoutRecord;
-import com.android.server.am.ActivityManagerService;
+import com.android.server.am.StackTracesDumpHelper;
import com.android.server.criticalevents.CriticalEventLog;
import java.io.File;
@@ -336,7 +336,7 @@ class AnrController {
String criticalEvents =
CriticalEventLog.getInstance().logLinesForSystemServerTraceFile();
- final File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
+ final File tracesFile = StackTracesDumpHelper.dumpStackTraces(firstPids,
null /* processCpuTracker */, null /* lastPids */,
CompletableFuture.completedFuture(nativePids),
null /* logExceptionCreatingFile */, "Pre-dump", criticalEvents,
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index b67bc62e52f1..fb79c0677a2f 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -262,14 +262,23 @@ class BackNavigationController {
if (!isOccluded || prevActivity.canShowWhenLocked()) {
// We have another Activity in the same currentTask to go to
final WindowContainer parent = currentActivity.getParent();
- final boolean isCustomize = parent != null
+ final boolean canCustomize = parent != null
&& (parent.asTask() != null
|| (parent.asTaskFragment() != null
- && parent.canCustomizeAppTransition()))
- && isCustomizeExitAnimation(window);
- if (isCustomize) {
- infoBuilder.setWindowAnimations(
- window.mAttrs.packageName, window.mAttrs.windowAnimations);
+ && parent.canCustomizeAppTransition()));
+ if (canCustomize) {
+ if (isCustomizeExitAnimation(window)) {
+ infoBuilder.setWindowAnimations(
+ window.mAttrs.packageName, window.mAttrs.windowAnimations);
+ }
+ final ActivityRecord.CustomAppTransition customAppTransition =
+ currentActivity.getCustomAnimation(false/* open */);
+ if (customAppTransition != null) {
+ infoBuilder.setCustomAnimation(currentActivity.packageName,
+ customAppTransition.mExitAnim,
+ customAppTransition.mEnterAnim,
+ customAppTransition.mBackgroundColor);
+ }
}
removedWindowContainer = currentActivity;
prevTask = prevActivity.getTask();