1 /* 2 * Copyright 2024 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 android.view; 18 19 import static android.Manifest.permission.DETECT_SCREEN_RECORDING; 20 import static android.view.WindowManager.SCREEN_RECORDING_STATE_NOT_VISIBLE; 21 import static android.view.WindowManager.SCREEN_RECORDING_STATE_VISIBLE; 22 23 import android.annotation.CallbackExecutor; 24 import android.annotation.NonNull; 25 import android.annotation.RequiresPermission; 26 import android.os.Binder; 27 import android.os.RemoteException; 28 import android.util.ArrayMap; 29 import android.view.WindowManager.ScreenRecordingState; 30 import android.window.IScreenRecordingCallback; 31 32 import java.util.ArrayList; 33 import java.util.List; 34 import java.util.Objects; 35 import java.util.concurrent.Executor; 36 import java.util.function.Consumer; 37 38 /** 39 * This class is responsible for calling app-registered screen recording callbacks. This class 40 * registers a single screen recording callback with WindowManagerService and calls the 41 * app-registered callbacks whenever that WindowManagerService callback is called. 42 * 43 * @hide 44 */ 45 public final class ScreenRecordingCallbacks { 46 47 private static ScreenRecordingCallbacks sInstance; 48 private static final Object sLock = new Object(); 49 50 private final ArrayMap<Consumer<@ScreenRecordingState Integer>, Executor> mCallbacks = 51 new ArrayMap<>(); 52 53 private IScreenRecordingCallback mCallbackNotifier; 54 private @ScreenRecordingState int mState = SCREEN_RECORDING_STATE_NOT_VISIBLE; 55 ScreenRecordingCallbacks()56 private ScreenRecordingCallbacks() {} 57 getWindowManagerService()58 private static @NonNull IWindowManager getWindowManagerService() { 59 return Objects.requireNonNull(WindowManagerGlobal.getWindowManagerService()); 60 } 61 getInstance()62 static ScreenRecordingCallbacks getInstance() { 63 synchronized (sLock) { 64 if (sInstance == null) { 65 sInstance = new ScreenRecordingCallbacks(); 66 } 67 return sInstance; 68 } 69 } 70 71 @RequiresPermission(DETECT_SCREEN_RECORDING) 72 @ScreenRecordingState addCallback( @onNull @allbackExecutor Executor executor, @NonNull Consumer<@ScreenRecordingState Integer> callback)73 int addCallback( 74 @NonNull @CallbackExecutor Executor executor, 75 @NonNull Consumer<@ScreenRecordingState Integer> callback) { 76 synchronized (sLock) { 77 if (mCallbackNotifier == null) { 78 mCallbackNotifier = 79 new IScreenRecordingCallback.Stub() { 80 @Override 81 public void onScreenRecordingStateChanged( 82 boolean visibleInScreenRecording) { 83 int state = 84 visibleInScreenRecording 85 ? SCREEN_RECORDING_STATE_VISIBLE 86 : SCREEN_RECORDING_STATE_NOT_VISIBLE; 87 notifyCallbacks(state); 88 } 89 }; 90 try { 91 boolean visibleInScreenRecording = 92 getWindowManagerService() 93 .registerScreenRecordingCallback(mCallbackNotifier); 94 mState = 95 visibleInScreenRecording 96 ? SCREEN_RECORDING_STATE_VISIBLE 97 : SCREEN_RECORDING_STATE_NOT_VISIBLE; 98 } catch (RemoteException e) { 99 e.rethrowFromSystemServer(); 100 } 101 } 102 mCallbacks.put(callback, executor); 103 return mState; 104 } 105 } 106 107 @RequiresPermission(DETECT_SCREEN_RECORDING) removeCallback(@onNull Consumer<@ScreenRecordingState Integer> callback)108 void removeCallback(@NonNull Consumer<@ScreenRecordingState Integer> callback) { 109 synchronized (sLock) { 110 mCallbacks.remove(callback); 111 if (mCallbacks.isEmpty()) { 112 try { 113 getWindowManagerService().unregisterScreenRecordingCallback(mCallbackNotifier); 114 } catch (RemoteException e) { 115 e.rethrowFromSystemServer(); 116 } 117 mCallbackNotifier = null; 118 } 119 } 120 } 121 notifyCallbacks(@creenRecordingState int state)122 private void notifyCallbacks(@ScreenRecordingState int state) { 123 List<Runnable> callbacks; 124 synchronized (sLock) { 125 mState = state; 126 if (mCallbacks.isEmpty()) { 127 return; 128 } 129 130 callbacks = new ArrayList<>(); 131 for (int i = 0; i < mCallbacks.size(); i++) { 132 Consumer<Integer> callback = mCallbacks.keyAt(i); 133 Executor executor = mCallbacks.valueAt(i); 134 callbacks.add(() -> executor.execute(() -> callback.accept(state))); 135 } 136 } 137 final long token = Binder.clearCallingIdentity(); 138 try { 139 for (int i = 0; i < callbacks.size(); i++) { 140 callbacks.get(i).run(); 141 } 142 } finally { 143 Binder.restoreCallingIdentity(token); 144 } 145 } 146 } 147