• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.mediaprojection
18 
19 import android.content.Context
20 import android.media.projection.IMediaProjection
21 import android.media.projection.IMediaProjectionManager
22 import android.media.projection.MediaProjectionManager
23 import android.media.projection.ReviewGrantedConsentResult
24 import android.os.RemoteException
25 import android.os.ServiceManager
26 import android.util.Log
27 import android.window.WindowContainerToken
28 import javax.inject.Inject
29 
30 /**
31  * Helper class that handles the media projection service related actions. It simplifies invoking
32  * the MediaProjectionManagerService and updating the permission consent.
33  */
34 class MediaProjectionServiceHelper @Inject constructor() {
35     companion object {
36         private const val TAG = "MediaProjectionServiceHelper"
37         private val service =
38             IMediaProjectionManager.Stub.asInterface(
39                 ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE)
40             )
41 
42         @JvmStatic
43         @Throws(RemoteException::class)
hasProjectionPermissionnull44         fun hasProjectionPermission(uid: Int, packageName: String) =
45             service.hasProjectionPermission(uid, packageName)
46 
47         @JvmStatic
48         @Throws(RemoteException::class)
49         fun createOrReuseProjection(
50             uid: Int,
51             packageName: String,
52             reviewGrantedConsentRequired: Boolean,
53             displayId: Int,
54         ): IMediaProjection {
55             val existingProjection =
56                 if (reviewGrantedConsentRequired) service.getProjection(uid, packageName) else null
57             return existingProjection
58                 ?: service.createProjection(
59                     uid,
60                     packageName,
61                     MediaProjectionManager.TYPE_SCREEN_CAPTURE,
62                     false /* permanentGrant */,
63                     displayId,
64                 )
65         }
66 
67         /**
68          * This method is called when a host app reuses the consent token. If the token is being
69          * used more than once, ask the user to review their consent and send the reviewed result.
70          *
71          * @param consentResult consent result to update
72          * @param reviewGrantedConsentRequired if user must review already-granted consent that the
73          *   host app is attempting to reuse
74          * @param projection projection token associated with the consent result, or null if the
75          *   result is for cancelling.
76          */
77         @JvmStatic
setReviewedConsentIfNeedednull78         fun setReviewedConsentIfNeeded(
79             @ReviewGrantedConsentResult consentResult: Int,
80             reviewGrantedConsentRequired: Boolean,
81             projection: IMediaProjection?,
82         ) {
83             // Only send the result to the server, when the user needed to review the re-used
84             // consent token.
85             if (
86                 reviewGrantedConsentRequired && consentResult != ReviewGrantedConsentResult.UNKNOWN
87             ) {
88                 try {
89                     service.setUserReviewGrantedConsentResult(consentResult, projection)
90                 } catch (e: RemoteException) {
91                     // If we are unable to pass back the result, capture continues with blank frames
92                     Log.e(TAG, "Unable to set required consent result for token re-use", e)
93                 }
94             }
95         }
96     }
97 
98     /** Updates the projected task to the task that has a matching [WindowContainerToken]. */
updateTaskRecordingSessionnull99     fun updateTaskRecordingSession(token: WindowContainerToken): Boolean {
100         return try {
101             true
102             // TODO: actually call the service once it is implemented
103             // service.updateTaskRecordingSession(token)
104         } catch (e: RemoteException) {
105             Log.e(TAG, "Unable to updateTaskRecordingSession", e)
106             false
107         }
108     }
109 }
110