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