Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7bcd1038 authored by Stefan Andonian's avatar Stefan Andonian Committed by Android (Google) Code Review
Browse files

Merge "Write ViewCapture data to WmTrace when WindowManager dumps." into udc-dev

parents 86e62654 3c492d86
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -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);
}
+28 −0
Original line number Diff line number Diff line
@@ -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;
@@ -1171,6 +1173,32 @@ public class LauncherApps {
        return shortcuts;
    }

    /**
     * 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}.
     *
+24 −0
Original line number Diff line number Diff line
/*
 * 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
+86 −0
Original line number Diff line number Diff line
@@ -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,