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

Commit fc3624a5 authored by Marvin Ramin's avatar Marvin Ramin
Browse files

Add history to VDM dumpsys

Introduces VirtualDeviceLog which keeps a few parameters for each
VirtualDevice (uid, creation and closing time).

This is added to dumpsys virtualdevice to help debug issues that may
have happened while a VD is active but bugreports may not show the VD
active anymore.

Whether VirtualDeviceLog is used is flagged by Flag dump_history.

Bug: 293114719
Test: dumpsys virtualdevice

Change-Id: Ia295eef9d8dbca2ce6c23e9ee59ddcba9e18790b
parent 8e3761ce
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -26,5 +26,6 @@ java_library_static {
    ],
    static_libs: [
        "ukey2_jni",
        "virtualdevice_flags_lib",
    ],
}
+7 −0
Original line number Diff line number Diff line
@@ -126,6 +126,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
    private final VirtualDeviceManagerService mService;
    private final PendingTrampolineCallback mPendingTrampolineCallback;
    private final int mOwnerUid;
    private final VirtualDeviceLog mVirtualDeviceLog;
    private final String mOwnerPackageName;
    private int mDeviceId;
    private @Nullable String mPersistentDeviceId;
@@ -197,6 +198,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
            Context context,
            AssociationInfo associationInfo,
            VirtualDeviceManagerService service,
            VirtualDeviceLog virtualDeviceLog,
            IBinder token,
            AttributionSource attributionSource,
            int deviceId,
@@ -210,6 +212,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
                context,
                associationInfo,
                service,
                virtualDeviceLog,
                token,
                attributionSource,
                deviceId,
@@ -228,6 +231,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
            Context context,
            AssociationInfo associationInfo,
            VirtualDeviceManagerService service,
            VirtualDeviceLog virtualDeviceLog,
            IBinder token,
            AttributionSource attributionSource,
            int deviceId,
@@ -240,6 +244,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
            VirtualDeviceParams params,
            DisplayManagerGlobal displayManager) {
        super(PermissionEnforcer.fromContext(context));
        mVirtualDeviceLog = virtualDeviceLog;
        mOwnerPackageName = attributionSource.getPackageName();
        UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(attributionSource.getUid());
        mContext = context.createContextAsUser(ownerUserHandle, 0);
@@ -271,6 +276,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        mVirtualDeviceLog.logCreated(deviceId, mOwnerUid);
    }

    @VisibleForTesting
@@ -405,6 +411,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
        super.close_enforcePermission();
        // Remove about-to-be-closed virtual device from the service before butchering it.
        boolean removed = mService.removeVirtualDevice(mDeviceId);
        mVirtualDeviceLog.logClosed(mDeviceId, mOwnerUid);
        mDeviceId = Context.DEVICE_ID_INVALID;

        // Device is already closed.
+152 −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 com.android.server.companion.virtual;

import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.util.SparseArray;

import java.io.PrintWriter;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayDeque;

final class VirtualDeviceLog {
    public static int TYPE_CREATED = 0x0;
    public static int TYPE_CLOSED = 0x1;

    private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern(
            "MM-dd HH:mm:ss.SSS").withZone(ZoneId.systemDefault());
    private static final int MAX_ENTRIES = 16;

    private final Context mContext;
    private final ArrayDeque<LogEntry> mLogEntries = new ArrayDeque<>();

    VirtualDeviceLog(Context context) {
        mContext = context;
    }

    void logCreated(int deviceId, int ownerUid) {
        final long token = Binder.clearCallingIdentity();
        try {
            if (!Flags.dumpHistory()) {
                return;
            }
            addEntry(new LogEntry(TYPE_CREATED, deviceId, System.currentTimeMillis(), ownerUid));
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    void logClosed(int deviceId, int ownerUid) {
        final long token = Binder.clearCallingIdentity();
        try {
            if (!Flags.dumpHistory()) {
                return;
            }
            addEntry(new LogEntry(TYPE_CLOSED, deviceId, System.currentTimeMillis(), ownerUid));
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    private void addEntry(LogEntry entry) {
        mLogEntries.push(entry);
        if (mLogEntries.size() > MAX_ENTRIES) {
            mLogEntries.removeLast();
        }
    }

    void dump(PrintWriter pw) {
        final long token = Binder.clearCallingIdentity();
        try {
            if (!Flags.dumpHistory()) {
                return;
            }
            pw.println("VirtualDevice Log:");
            UidToPackageNameCache packageNameCache = new UidToPackageNameCache(
                    mContext.getPackageManager());
            for (LogEntry logEntry : mLogEntries) {
                logEntry.dump(pw, "  ", packageNameCache);

            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    static class LogEntry {
        private final int mType;
        private final int mDeviceId;
        private final long mTimestamp;
        private final int mOwnerUid;

        LogEntry(int type, int deviceId, long timestamp, int ownerUid) {
            this.mType = type;
            this.mDeviceId = deviceId;
            this.mTimestamp = timestamp;
            this.mOwnerUid = ownerUid;
        }

        void dump(PrintWriter pw, String prefix, UidToPackageNameCache packageNameCache) {
            StringBuilder sb = new StringBuilder(prefix);
            sb.append(DATE_FORMAT.format(Instant.ofEpochMilli(mTimestamp)));
            sb.append(" - ");
            sb.append(mType == TYPE_CREATED ? "START" : "CLOSE");
            sb.append(" Device ID: ");
            sb.append(mDeviceId);
            sb.append(" ");
            sb.append(mOwnerUid);
            sb.append(" (");
            sb.append(packageNameCache.getPackageName(mOwnerUid));
            sb.append(")");
            pw.println(sb);
        }
    }

    private static class UidToPackageNameCache {
        private final PackageManager mPackageManager;
        private final SparseArray<String> mUidToPackagesCache = new SparseArray<>();

        public UidToPackageNameCache(PackageManager packageManager) {
            mPackageManager = packageManager;
        }

        String getPackageName(int ownerUid) {
            String[] packages;
            if (mUidToPackagesCache.contains(ownerUid)) {
                return mUidToPackagesCache.get(ownerUid);
            } else {
                packages = mPackageManager.getPackagesForUid(ownerUid);
                String packageName = "";
                if (packages != null && packages.length > 0) {
                    packageName = packages[0];
                    if (packages.length > 1) {
                        StringBuilder sb = new StringBuilder();
                        sb.append(packageName)
                                .append(",...");
                        packageName = sb.toString();
                    }
                }
                mUidToPackagesCache.put(ownerUid, packageName);
                return packageName;
            }
        }
    }
}
+6 −1
Original line number Diff line number Diff line
@@ -85,6 +85,7 @@ public class VirtualDeviceManagerService extends SystemService {
    private final Object mVirtualDeviceManagerLock = new Object();
    private final VirtualDeviceManagerImpl mImpl;
    private final VirtualDeviceManagerInternal mLocalService;
    private VirtualDeviceLog mVirtualDeviceLog = new VirtualDeviceLog(getContext());
    private final Handler mHandler = new Handler(Looper.getMainLooper());
    private final PendingTrampolineMap mPendingTrampolines = new PendingTrampolineMap(mHandler);

@@ -344,9 +345,11 @@ public class VirtualDeviceManagerService extends SystemService {
            final Consumer<ArraySet<Integer>> runningAppsChangedCallback =
                    runningUids -> notifyRunningAppsChanged(deviceId, runningUids);
            VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(), associationInfo,
                    VirtualDeviceManagerService.this, token, attributionSource, deviceId,
                    VirtualDeviceManagerService.this, mVirtualDeviceLog, token, attributionSource,
                    deviceId,
                    cameraAccessController, mPendingTrampolineCallback, activityListener,
                    soundEffectListener, runningAppsChangedCallback, params);

            synchronized (mVirtualDeviceManagerLock) {
                if (mVirtualDevices.size() == 0) {
                    final long callindId = Binder.clearCallingIdentity();
@@ -521,6 +524,8 @@ public class VirtualDeviceManagerService extends SystemService {
            for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
                virtualDevicesSnapshot.get(i).dump(fd, fout, args);
            }

            mVirtualDeviceLog.dump(fout);
        }
    }

+3 −1
Original line number Diff line number Diff line
@@ -226,6 +226,7 @@ public class VirtualDeviceManagerServiceTest {
    private VirtualDeviceManagerService mVdms;
    private VirtualDeviceManagerInternal mLocalService;
    private VirtualDeviceManagerService.VirtualDeviceManagerImpl mVdm;
    private VirtualDeviceLog mVirtualDeviceLog;
    @Mock
    private InputController.NativeWrapper mNativeWrapperMock;
    @Mock
@@ -370,6 +371,7 @@ public class VirtualDeviceManagerServiceTest {
        mVdms = new VirtualDeviceManagerService(mContext);
        mLocalService = mVdms.getLocalServiceInstance();
        mVdm = mVdms.new VirtualDeviceManagerImpl();
        mVirtualDeviceLog = new VirtualDeviceLog(mContext);
        mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1);
        mSensorController = mDeviceImpl.getSensorControllerForTest();
    }
@@ -1730,7 +1732,7 @@ public class VirtualDeviceManagerServiceTest {
    private VirtualDeviceImpl createVirtualDevice(int virtualDeviceId, int ownerUid,
            VirtualDeviceParams params) {
        VirtualDeviceImpl virtualDeviceImpl = new VirtualDeviceImpl(mContext,
                mAssociationInfo, mVdms, new Binder(),
                mAssociationInfo, mVdms, mVirtualDeviceLog, new Binder(),
                new AttributionSource(ownerUid, "com.android.virtualdevice.test", "virtualdevice"),
                virtualDeviceId,
                mInputController, mCameraAccessController,
+1 −1

File changed.

Contains only whitespace changes.

Loading