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

Commit 08263864 authored by mrulhania's avatar mrulhania
Browse files

Log permission one time session events

These events will be used to analyze one
time permission session length to understand
how apps are using one time permissions.

Bug: 417779908
Test: presubmit
Flag: android.permission.flags.permission_one_time_session_logging_enabled
Change-Id: I01758bbef58830e765999d2ce27d10f4d0803e7e
parent 160e1c04
Loading
Loading
Loading
Loading
+77 −2
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server.pm.permission;

import static android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME;

import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -23,6 +25,8 @@ import android.app.AlarmManager;
import android.app.IActivityManager;
import android.app.IUidObserver;
import android.app.UidObserver;
import android.companion.virtual.VirtualDevice;
import android.companion.virtual.VirtualDeviceManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -30,14 +34,24 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.RemoteException;
import android.os.SystemClock;
import android.permission.PermissionControllerManager;
import android.permission.PermissionManager;
import android.permission.flags.Flags;
import android.provider.DeviceConfig;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.PermissionThread;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Class that handles one-time permissions for a user
@@ -56,6 +70,8 @@ public class OneTimePermissionUserManager {
    private final @NonNull ActivityManagerInternal mActivityManagerInternal;
    private final @NonNull AlarmManager mAlarmManager;
    private final @NonNull PermissionControllerManager mPermissionControllerManager;
    private final @NonNull PermissionManager mPermissionManager;
    private final VirtualDeviceManagerInternal mVirtualDeviceManagerInternal;

    private final Object mLock = new Object();

@@ -72,6 +88,7 @@ public class OneTimePermissionUserManager {
                        }
                        listener.cancel();
                        mListeners.remove(uid);
                        mHandler.post(() -> logOneTimeSessionEvent(listener));
                    }
                }
            }
@@ -90,6 +107,9 @@ public class OneTimePermissionUserManager {
        mAlarmManager = context.getSystemService(AlarmManager.class);
        mPermissionControllerManager = new PermissionControllerManager(
                mContext, PermissionThread.getHandler());
        mPermissionManager = mContext.getSystemService(PermissionManager.class);
        mVirtualDeviceManagerInternal =
                LocalServices.getService(VirtualDeviceManagerInternal.class);
        mHandler = context.getMainThreadHandler();
    }

@@ -136,8 +156,10 @@ public class OneTimePermissionUserManager {
            if (listener != null) {
                mListeners.remove(uid);
                listener.cancel();
                mHandler.post(() -> logOneTimeSessionEvent(listener));
            }
        }

    }

    /**
@@ -148,6 +170,19 @@ public class OneTimePermissionUserManager {
        mContext.registerReceiver(mUninstallListener, new IntentFilter(Intent.ACTION_UID_REMOVED));
    }

    /**
     * Log the session duration when the session is finished.
     *
     * @param listener listener for which the one time session is finished/expired.
     */
    private void logOneTimeSessionEvent(@NonNull PackageInactivityListener listener) {
        if (Flags.permissionOneTimeSessionLoggingEnabled()) {
            long durationMillis = SystemClock.elapsedRealtime() - listener.mSessionStartMillis;
            FrameworkStatsLog.write(FrameworkStatsLog.PERMISSION_ONE_TIME_SESSION_EVENT_REPORTED,
                    listener.mUid, listener.mPermissions.toArray(new String[0]), durationMillis);
        }
    }

    /**
     * A class which watches a package for inactivity and notifies the permission controller when
     * the package becomes inactive
@@ -173,6 +208,11 @@ public class OneTimePermissionUserManager {

        private final Object mInnerLock = new Object();
        private final Object mToken = new Object();
        // timestamp to track when the one time session starts.
        private final long mSessionStartMillis = SystemClock.elapsedRealtime();
        // Permissions for which the session is started, used in logging
        private final ArraySet<String> mPermissions = new ArraySet<>();

        private final IUidObserver mObserver = new UidObserver() {
            @Override
            public void onUidGone(int uid, boolean disabled) {
@@ -211,6 +251,10 @@ public class OneTimePermissionUserManager {
                            DEFAULT_KILLED_DELAY_MILLIS)
                    : revokeAfterkilledDelay;

            if (Flags.permissionOneTimeSessionLoggingEnabled()) {
                mPermissions.addAll(getOneTimePermissions(packageName, deviceId));
            }

            try {
                mIActivityManager.registerUidObserver(mObserver,
                        ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_PROCSTATE,
@@ -227,6 +271,33 @@ public class OneTimePermissionUserManager {
            updateUidState();
        }

        private @NonNull List<String> getOneTimePermissions(@NonNull String packageName,
                int deviceId) {
            List<String> permissions = new ArrayList<>();
            Map<String, PermissionManager.PermissionState> permissionStates =
                    mPermissionManager.getAllPermissionStates(packageName,
                            getPersistentDeviceId(deviceId));
            for (Map.Entry<String, PermissionManager.PermissionState> entry :
                    permissionStates.entrySet()) {
                PermissionManager.PermissionState permissionState = entry.getValue();
                if (permissionState.isGranted()
                        && (permissionState.getFlags() & FLAG_PERMISSION_ONE_TIME) != 0) {
                    permissions.add(entry.getKey());
                }
            }
            return permissions;
        }

        private @NonNull String getPersistentDeviceId(int deviceId) {
            VirtualDevice virtualDevice = mVirtualDeviceManagerInternal.getVirtualDevice(deviceId);
            String persistentDeviceId = null;
            if (virtualDevice != null) {
                persistentDeviceId = virtualDevice.getPersistentDeviceId();
            }
            return persistentDeviceId != null ? persistentDeviceId
                    : VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
        }

        public void updateSessionParameters(long timeoutMillis, long revokeAfterKilledDelayMillis) {
            synchronized (mInnerLock) {
                mTimeout = Math.min(mTimeout, timeoutMillis);
@@ -242,6 +313,9 @@ public class OneTimePermissionUserManager {
                                + " killedDelay=" + mRevokeAfterKilledDelay);
                updateUidState();
            }
            if (Flags.permissionOneTimeSessionLoggingEnabled()) {
                mPermissions.addAll(getOneTimePermissions(mPackageName, mDeviceId));
            }
        }

        private int getCurrentState() {
@@ -394,6 +468,7 @@ public class OneTimePermissionUserManager {
            synchronized (mLock) {
                mListeners.remove(mUid);
            }
            mHandler.post(() -> logOneTimeSessionEvent(this));
        }

        @Override