diff options
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, |