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 ): IMediaProjection { 54 val existingProjection = 55 if (reviewGrantedConsentRequired) service.getProjection(uid, packageName) else null 56 return existingProjection 57 ?: service.createProjection( 58 uid, 59 packageName, 60 MediaProjectionManager.TYPE_SCREEN_CAPTURE, 61 false /* permanentGrant */ 62 ) 63 } 64 65 /** 66 * This method is called when a host app reuses the consent token. If the token is being 67 * used more than once, ask the user to review their consent and send the reviewed result. 68 * 69 * @param consentResult consent result to update 70 * @param reviewGrantedConsentRequired if user must review already-granted consent that the 71 * host app is attempting to reuse 72 * @param projection projection token associated with the consent result, or null if the 73 * result is for cancelling. 74 */ 75 @JvmStatic setReviewedConsentIfNeedednull76 fun setReviewedConsentIfNeeded( 77 @ReviewGrantedConsentResult consentResult: Int, 78 reviewGrantedConsentRequired: Boolean, 79 projection: IMediaProjection? 80 ) { 81 // Only send the result to the server, when the user needed to review the re-used 82 // consent token. 83 if ( 84 reviewGrantedConsentRequired && consentResult != ReviewGrantedConsentResult.UNKNOWN 85 ) { 86 try { 87 service.setUserReviewGrantedConsentResult(consentResult, projection) 88 } catch (e: RemoteException) { 89 // If we are unable to pass back the result, capture continues with blank frames 90 Log.e(TAG, "Unable to set required consent result for token re-use", e) 91 } 92 } 93 } 94 } 95 96 /** Updates the projected task to the task that has a matching [WindowContainerToken]. */ updateTaskRecordingSessionnull97 fun updateTaskRecordingSession(token: WindowContainerToken): Boolean { 98 return try { 99 true 100 // TODO: actually call the service once it is implemented 101 // service.updateTaskRecordingSession(token) 102 } catch (e: RemoteException) { 103 Log.e(TAG, "Unable to updateTaskRecordingSession", e) 104 false 105 } 106 } 107 } 108