/* * Copyright (C) 2022 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 android.app; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.os.IBinder; import android.os.RemoteException; import android.util.ArrayMap; import java.util.concurrent.Executor; /** Handles screen capture callbacks. * @hide **/ public class ScreenCaptureCallbackHandler { private final IBinder mActivityToken; private final ScreenCaptureObserver mObserver; private final ArrayMap mScreenCaptureRegistrations = new ArrayMap<>(); public ScreenCaptureCallbackHandler(@NonNull IBinder activityToken) { mActivityToken = activityToken; mObserver = new ScreenCaptureObserver(mScreenCaptureRegistrations); } private static class ScreenCaptureRegistration { Executor mExecutor; Activity.ScreenCaptureCallback mCallback; ScreenCaptureRegistration(Executor executor, Activity.ScreenCaptureCallback callback) { this.mExecutor = executor; this.mCallback = callback; } } private static class ScreenCaptureObserver extends IScreenCaptureObserver.Stub { ArrayMap mRegistrations; ScreenCaptureObserver( ArrayMap registrations) { this.mRegistrations = registrations; } @Override public void onScreenCaptured() { for (ScreenCaptureRegistration registration : mRegistrations.values()) { registration.mExecutor.execute( () -> { registration.mCallback.onScreenCaptured(); }); } } } /** * Start monitoring for screen captures of the activity, the callback will be triggered whenever * a screen capture is attempted. This callback will be executed on the thread of the passed * {@code executor}. If the window is FLAG_SECURE, the callback will not be triggered. */ public void registerScreenCaptureCallback( @NonNull @CallbackExecutor Executor executor, @NonNull Activity.ScreenCaptureCallback callback) { ScreenCaptureRegistration registration = new ScreenCaptureRegistration(executor, callback); synchronized (mScreenCaptureRegistrations) { if (mScreenCaptureRegistrations.containsKey(callback)) { throw new IllegalStateException( "Capture observer already registered with the activity"); } mScreenCaptureRegistrations.put(callback, registration); // register with system server only once. if (mScreenCaptureRegistrations.size() == 1) { try { ActivityTaskManager.getService() .registerScreenCaptureObserver(mActivityToken, mObserver); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } } } /** Stop monitoring for screen captures of the activity */ public void unregisterScreenCaptureCallback(@NonNull Activity.ScreenCaptureCallback callback) { synchronized (mScreenCaptureRegistrations) { if (!mScreenCaptureRegistrations.containsKey(callback)) { throw new IllegalStateException( "Capture observer not registered with the activity"); } mScreenCaptureRegistrations.remove(callback); // unregister only if no more registrations are left if (mScreenCaptureRegistrations.size() == 0) { try { ActivityTaskManager.getService().unregisterScreenCaptureObserver(mActivityToken, mObserver); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } } } }