summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/content/pm/ILauncherApps.aidl7
-rw-r--r--core/java/android/content/pm/LauncherApps.java28
-rw-r--r--core/java/android/window/IDumpCallback.aidl24
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java86
4 files changed, 145 insertions, 0 deletions
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 96a42e24bc1a..563ed7dd6e7a 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -38,6 +38,7 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.ParcelFileDescriptor;
+import android.window.IDumpCallback;
import com.android.internal.infra.AndroidFuture;
@@ -116,4 +117,10 @@ interface ILauncherApps {
String getShortcutIconUri(String callingPackage, String packageName, String shortcutId,
int userId);
Map<String, LauncherActivityInfoInternal> getActivityOverrides(String callingPackage, int userId);
+
+ /** Register a callback to be called right before the wmtrace data is moved to the bugreport. */
+ void registerDumpCallback(IDumpCallback cb);
+
+ /** Unregister a callback, so that it won't be called when LauncherApps dumps. */
+ void unRegisterDumpCallback(IDumpCallback cb);
}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 8989006a7e83..27270d9f378f 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -17,6 +17,7 @@
package android.content.pm;
import static android.Manifest.permission;
+import static android.Manifest.permission.READ_FRAME_BUFFER;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
@@ -68,6 +69,7 @@ import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
+import android.window.IDumpCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.infra.AndroidFuture;
@@ -1172,6 +1174,32 @@ public class LauncherApps {
}
/**
+ * Register a callback to be called right before the wmtrace data is moved to the bugreport.
+ * @hide
+ */
+ @RequiresPermission(READ_FRAME_BUFFER)
+ public void registerDumpCallback(IDumpCallback cb) {
+ try {
+ mService.registerDumpCallback(cb);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Unregister a callback, so that it won't be called when LauncherApps dumps.
+ * @hide
+ */
+ @RequiresPermission(READ_FRAME_BUFFER)
+ public void unRegisterDumpCallback(IDumpCallback cb) {
+ try {
+ mService.unRegisterDumpCallback(cb);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
* Returns {@link ShortcutInfo}s that match {@code query}.
*
* <p>Callers must be allowed to access the shortcut information, as defined in {@link
diff --git a/core/java/android/window/IDumpCallback.aidl b/core/java/android/window/IDumpCallback.aidl
new file mode 100644
index 000000000000..4c825d43add1
--- /dev/null
+++ b/core/java/android/window/IDumpCallback.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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 android.window;
+
+/**
+ * Callback for processes which need to feed data to another process when it dumps.
+ * @hide
+ */
+ interface IDumpCallback {
+ oneway void onDump(in ParcelFileDescriptor outFd);
+ } \ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 84bee50b77b0..402fb30437b0 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -16,6 +16,7 @@
package com.android.server.pm;
+import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.app.ActivityOptions.KEY_SPLASH_SCREEN_THEME;
import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
@@ -25,6 +26,8 @@ import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
+import static android.content.PermissionChecker.PERMISSION_GRANTED;
+import static android.content.PermissionChecker.checkCallingOrSelfPermissionForPreflight;
import static android.content.pm.LauncherApps.FLAG_CACHE_BUBBLE_SHORTCUTS;
import static android.content.pm.LauncherApps.FLAG_CACHE_NOTIFICATION_SHORTCUTS;
import static android.content.pm.LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS;
@@ -32,6 +35,7 @@ import static android.content.pm.LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS;
import android.annotation.AppIdInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -90,6 +94,7 @@ import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
+import android.window.IDumpCallback;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -104,6 +109,15 @@ import com.android.server.SystemService;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.wm.ActivityTaskManagerInternal;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.PosixFilePermission;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -111,6 +125,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
/**
@@ -118,6 +133,15 @@ import java.util.concurrent.ExecutionException;
* managed profiles.
*/
public class LauncherAppsService extends SystemService {
+ private static final String WM_TRACE_DIR = "/data/misc/wmtrace/";
+ private static final String VC_FILE_SUFFIX = ".vc";
+
+ private static final Set<PosixFilePermission> WM_TRACE_FILE_PERMISSIONS = Set.of(
+ PosixFilePermission.OWNER_WRITE,
+ PosixFilePermission.GROUP_READ,
+ PosixFilePermission.OTHERS_READ,
+ PosixFilePermission.OWNER_READ
+ );
private final LauncherAppsImpl mLauncherAppsImpl;
@@ -191,6 +215,8 @@ public class LauncherAppsService extends SystemService {
final LauncherAppsServiceInternal mInternal;
+ private RemoteCallbackList<IDumpCallback> mDumpCallbacks = new RemoteCallbackList<>();
+
public LauncherAppsImpl(Context context) {
mContext = context;
mIPM = AppGlobals.getPackageManager();
@@ -1431,6 +1457,66 @@ public class LauncherAppsService extends SystemService {
getActivityOptionsForLauncher(opts), user.getIdentifier());
}
+
+ /**
+ * Using a pipe, outputs view capture data to the wmtrace dir
+ */
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ super.dump(fd, pw, args);
+
+ // Before the wmtrace directory is picked up by dumpstate service, some processes need
+ // to write their data to that location. They can do that via these dumpCallbacks.
+ int i = mDumpCallbacks.beginBroadcast();
+ while (i > 0) {
+ i--;
+ dumpDataToWmTrace((String) mDumpCallbacks.getBroadcastCookie(i) + "_" + i,
+ mDumpCallbacks.getBroadcastItem(i));
+ }
+ mDumpCallbacks.finishBroadcast();
+ }
+
+ private void dumpDataToWmTrace(String name, IDumpCallback cb) {
+ ParcelFileDescriptor[] pipe;
+ try {
+ pipe = ParcelFileDescriptor.createPipe();
+ cb.onDump(pipe[1]);
+ } catch (IOException | RemoteException e) {
+ Log.d(TAG, "failed to pipe view capture data", e);
+ return;
+ }
+
+ Path path = Paths.get(WM_TRACE_DIR + Paths.get(name + VC_FILE_SUFFIX).getFileName());
+ try (InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pipe[0])) {
+ Files.copy(is, path, StandardCopyOption.REPLACE_EXISTING);
+ Files.setPosixFilePermissions(path, WM_TRACE_FILE_PERMISSIONS);
+ } catch (IOException e) {
+ Log.d(TAG, "failed to write data to file in wmtrace dir", e);
+ }
+ }
+
+ @RequiresPermission(READ_FRAME_BUFFER)
+ @Override
+ public void registerDumpCallback(IDumpCallback cb) {
+ int status = checkCallingOrSelfPermissionForPreflight(mContext, READ_FRAME_BUFFER);
+ if (PERMISSION_GRANTED == status) {
+ String name = mContext.getPackageManager().getNameForUid(Binder.getCallingUid());
+ mDumpCallbacks.register(cb, name);
+ } else {
+ Log.w(TAG, "caller lacks permissions to registerDumpCallback");
+ }
+ }
+
+ @RequiresPermission(READ_FRAME_BUFFER)
+ @Override
+ public void unRegisterDumpCallback(IDumpCallback cb) {
+ int status = checkCallingOrSelfPermissionForPreflight(mContext, READ_FRAME_BUFFER);
+ if (PERMISSION_GRANTED == status) {
+ mDumpCallbacks.unregister(cb);
+ } else {
+ Log.w(TAG, "caller lacks permissions to unRegisterDumpCallback");
+ }
+ }
+
/** Checks if user is a profile of or same as listeningUser.
* and the user is enabled. */
private boolean isEnabledProfileOf(UserHandle listeningUser, UserHandle user,