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

Commit cbff326c authored by Sam Cackett's avatar Sam Cackett Committed by Android (Google) Code Review
Browse files

Merge "[MediaProjection] Add tracking foundational classes" into main

parents 955f9d2d 99a7f3fb
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -156,4 +156,24 @@ interface IMediaProjectionManager {
            + ".permission.MANAGE_MEDIA_PROJECTION)")
    void setUserReviewGrantedConsentResult(ReviewGrantedConsentResult consentResult,
            in @nullable IMediaProjection projection);

    /**
     * Notifies system server that we are handling a particular state during the consent flow.
     *
     * <p>Only used for emitting atoms.
     *
     * @param hostUid               The uid of the process requesting consent to capture, may be an app or
     *                              SystemUI.
     * @param state                 The state that SystemUI is handling during the consent flow.
     *                              Must be a valid
     *                              state defined in the MediaProjectionState enum.
     * @param sessionCreationSource Only set if the state is MEDIA_PROJECTION_STATE_INITIATED.
     *                              Indicates the entry point for requesting the permission. Must be
     *                              a valid state defined
     *                              in the SessionCreationSource enum.
     */
    @EnforcePermission("android.Manifest.permission.MANAGE_MEDIA_PROJECTION")
    @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
            + ".permission.MANAGE_MEDIA_PROJECTION)")
    void notifyPermissionRequestStateChange(int hostUid, int state, int sessionCreationSource);
}
+8 −0
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ import android.hardware.input.InputManager;
import android.media.AudioManager;
import android.media.IAudioService;
import android.media.MediaRouter2Manager;
import android.media.projection.IMediaProjectionManager;
import android.media.projection.MediaProjectionManager;
import android.media.session.MediaSessionManager;
import android.net.ConnectivityManager;
@@ -413,6 +414,13 @@ public class FrameworkServicesModule {
        return context.getSystemService(MediaProjectionManager.class);
    }

    @Provides
    @Singleton
    static IMediaProjectionManager provideIMediaProjectionManager() {
        return IMediaProjectionManager.Stub.asInterface(
                ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE));
    }

    @Provides
    static MediaRouter2Manager provideMediaRouter2Manager(Context context) {
        return MediaRouter2Manager.getInstance(context);
+83 −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.systemui.mediaprojection

import android.media.projection.IMediaProjectionManager
import android.os.Process
import android.os.RemoteException
import android.util.Log
import com.android.internal.util.FrameworkStatsLog
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject

/**
 * Helper class for requesting that the server emit logs describing the MediaProjection setup
 * experience.
 */
@SysUISingleton
class MediaProjectionMetricsLogger
@Inject
constructor(private val service: IMediaProjectionManager) {
    /**
     * Request to log that the permission was requested.
     *
     * @param sessionCreationSource The entry point requesting permission to capture.
     */
    fun notifyPermissionProgress(state: Int, sessionCreationSource: Int) {
        // TODO check that state & SessionCreationSource matches expected values
        notifyToServer(state, sessionCreationSource)
    }

    /**
     * Request to log that the permission request moved to the given state.
     *
     * Should not be used for the initialization state, since that
     */
    fun notifyPermissionProgress(state: Int) {
        // TODO validate state is valid
        notifyToServer(
            state,
            FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN)
    }

    /**
     * Notifies system server that we are handling a particular state during the consent flow.
     *
     * Only used for emitting atoms.
     *
     * @param state The state that SystemUI is handling during the consent flow. Must be a valid
     *   state defined in the MediaProjectionState enum.
     * @param sessionCreationSource Only set if the state is MEDIA_PROJECTION_STATE_INITIATED.
     *   Indicates the entry point for requesting the permission. Must be a valid state defined in
     *   the SessionCreationSource enum.
     */
    private fun notifyToServer(state: Int, sessionCreationSource: Int) {
        Log.v(TAG, "FOO notifyToServer of state $state and source $sessionCreationSource")
        try {
            service.notifyPermissionRequestStateChange(
                Process.myUid(), state, sessionCreationSource)
        } catch (e: RemoteException) {
            Log.e(
                TAG,
                "Error notifying server of permission flow state $state from source $sessionCreationSource",
                e)
        }
    }

    companion object {
        const val TAG = "MediaProjectionMetricsLogger"
    }
}
+24 −0
Original line number Diff line number Diff line
@@ -76,6 +76,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.Watchdog;
@@ -134,6 +135,7 @@ public final class MediaProjectionManagerService extends SystemService

    private final MediaRouter mMediaRouter;
    private final MediaRouterCallback mMediaRouterCallback;
    private final MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
    private MediaRouter.RouteInfo mMediaRouteInfo;

    @GuardedBy("mLock")
@@ -160,6 +162,7 @@ public final class MediaProjectionManagerService extends SystemService
        mWmInternal = LocalServices.getService(WindowManagerInternal.class);
        mMediaRouter = (MediaRouter) mContext.getSystemService(Context.MEDIA_ROUTER_SERVICE);
        mMediaRouterCallback = new MediaRouterCallback();
        mMediaProjectionMetricsLogger = injector.mediaProjectionMetricsLogger();
        Watchdog.getInstance().addMonitor(this);
    }

@@ -193,6 +196,10 @@ public final class MediaProjectionManagerService extends SystemService
        Looper createCallbackLooper() {
            return Looper.getMainLooper();
        }

        MediaProjectionMetricsLogger mediaProjectionMetricsLogger() {
            return MediaProjectionMetricsLogger.getInstance();
        }
    }

    @Override
@@ -372,6 +379,10 @@ public final class MediaProjectionManagerService extends SystemService
            if (mProjectionGrant != null) {
                // Cache the session details.
                mProjectionGrant.mSession = incomingSession;
                mMediaProjectionMetricsLogger.notifyProjectionStateChange(
                        mProjectionGrant.uid,
                        FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS,
                        FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN);
                dispatchSessionSet(mProjectionGrant.getProjectionInfo(), incomingSession);
            }
            return true;
@@ -817,6 +828,19 @@ public final class MediaProjectionManagerService extends SystemService
            }
        }

        @Override // Binder call
        @EnforcePermission(MANAGE_MEDIA_PROJECTION)
        public void notifyPermissionRequestStateChange(int hostUid, int state,
                int sessionCreationSource) {
            notifyPermissionRequestStateChange_enforcePermission();
            final long token = Binder.clearCallingIdentity();
            try {
                mMediaProjectionMetricsLogger.notifyProjectionStateChange(hostUid, state, sessionCreationSource);
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

        @Override // Binder call
        public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
            if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+50 −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.media.projection;


import com.android.internal.util.FrameworkStatsLog;

/**
 * Class for emitting logs describing a MediaProjection session.
 */
public class MediaProjectionMetricsLogger {
    private static MediaProjectionMetricsLogger sSingleton = null;

    public static MediaProjectionMetricsLogger getInstance() {
        if (sSingleton == null) {
            sSingleton = new MediaProjectionMetricsLogger();
        }
        return sSingleton;
    }

    void notifyProjectionStateChange(int hostUid, int state, int sessionCreationSource) {
        write(hostUid, state, sessionCreationSource);
    }

    private void write(int hostUid, int state, int sessionCreationSource) {
        FrameworkStatsLog.write(
                /* code */ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED,
                /* session_id */ 123,
                /* state */ state,
                /* previous_state */ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN,
                /* host_uid */ hostUid,
                /* target_uid */ -1,
                /* time_since_last_active */ 0,
                /* creation_source */ sessionCreationSource);
    }
}
Loading