1 /* 2 * Copyright 2019 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 androidx.camera.core.impl; 18 19 import android.view.Surface; 20 21 import androidx.camera.core.impl.utils.futures.FutureCallback; 22 import androidx.camera.core.impl.utils.futures.Futures; 23 import androidx.concurrent.futures.CallbackToFutureAdapter; 24 import androidx.core.util.Preconditions; 25 26 import com.google.common.util.concurrent.ListenableFuture; 27 28 import org.jspecify.annotations.NonNull; 29 import org.jspecify.annotations.Nullable; 30 31 import java.util.ArrayList; 32 import java.util.Collection; 33 import java.util.Collections; 34 import java.util.List; 35 import java.util.concurrent.Executor; 36 import java.util.concurrent.ScheduledExecutorService; 37 import java.util.concurrent.TimeoutException; 38 39 /** 40 * Utility functions for manipulating {@link DeferrableSurface}. 41 */ 42 public final class DeferrableSurfaces { 43 DeferrableSurfaces()44 private DeferrableSurfaces() { 45 } 46 47 /** 48 * Returns a {@link ListenableFuture} that get the List<Surface> result form 49 * {@link DeferrableSurface} collection. 50 * 51 * @param removeNullSurfaces If true remove all Surfaces that were not retrieved. 52 * @param timeoutMillis The task timeout value in milliseconds. 53 * @param executor The executor service to run the task. 54 * @param scheduledExecutorService The executor service to schedule the timeout event. 55 */ surfaceListWithTimeout( @onNull Collection<DeferrableSurface> deferrableSurfaces, boolean removeNullSurfaces, long timeoutMillis, @NonNull Executor executor, @NonNull ScheduledExecutorService scheduledExecutorService)56 public static @NonNull ListenableFuture<List<Surface>> surfaceListWithTimeout( 57 @NonNull Collection<DeferrableSurface> deferrableSurfaces, 58 boolean removeNullSurfaces, long timeoutMillis, @NonNull Executor executor, 59 @NonNull ScheduledExecutorService scheduledExecutorService) { 60 List<ListenableFuture<Surface>> list = new ArrayList<>(); 61 for (DeferrableSurface surface : deferrableSurfaces) { 62 list.add(Futures.nonCancellationPropagating(surface.getSurface())); 63 } 64 ListenableFuture<List<Surface>> listenableFuture = Futures.makeTimeoutFuture( 65 timeoutMillis, scheduledExecutorService, Futures.successfulAsList(list) 66 ); 67 68 return CallbackToFutureAdapter.getFuture(completer -> { 69 // Cancel the listenableFuture if the outer task was cancelled, and the 70 // listenableFuture will cancel the scheduledFuture on its complete callback. 71 completer.addCancellationListener(() -> listenableFuture.cancel(true), executor); 72 73 Futures.addCallback(listenableFuture, new FutureCallback<List<Surface>>() { 74 @Override 75 public void onSuccess(@Nullable List<Surface> result) { 76 Preconditions.checkNotNull(result); 77 List<Surface> surfaces = new ArrayList<>(result); 78 if (removeNullSurfaces) { 79 surfaces.removeAll(Collections.singleton(null)); 80 } 81 completer.set(surfaces); 82 } 83 84 @Override 85 public void onFailure(@NonNull Throwable t) { 86 if (t instanceof TimeoutException) { 87 completer.setException(t); 88 } else { 89 completer.set(Collections.emptyList()); 90 } 91 } 92 }, executor); 93 94 return "surfaceList[" + deferrableSurfaces + "]"; 95 }); 96 } 97 98 /** 99 * Attempts to increment the usage count of all surfaces in the given surface list. 100 * 101 * <p>If any usage count fails to increment (due to the surface already being closed), then 102 * none of the surfaces in the list will have their usage count incremented. 103 * 104 * @param surfaceList The list of surfaces whose usage count should be incremented. 105 * @return {@code true} if all usage counts were successfully incremented, {@code false} 106 * otherwise. 107 */ tryIncrementAll(@onNull List<DeferrableSurface> surfaceList)108 public static boolean tryIncrementAll(@NonNull List<DeferrableSurface> surfaceList) { 109 try { 110 incrementAll(surfaceList); 111 } catch (DeferrableSurface.SurfaceClosedException e) { 112 return false; 113 } 114 115 return true; 116 } 117 118 /** 119 * Attempts to increment the usage count of all surfaces in the given surface list. 120 * 121 * <p>If any usage count fails to increment (due to the surface already being closed), then 122 * none of the surfaces in the list will have their usage count incremented and an exception 123 * will be thrown. 124 * 125 * @param surfaceList The list of surfaces whose usage count should be incremented. 126 * @throws DeferrableSurface.SurfaceClosedException Containing the surface that failed to 127 * increment. 128 */ incrementAll(@onNull List<DeferrableSurface> surfaceList)129 public static void incrementAll(@NonNull List<DeferrableSurface> surfaceList) 130 throws DeferrableSurface.SurfaceClosedException { 131 if (!surfaceList.isEmpty()) { 132 int i = 0; 133 try { 134 do { 135 surfaceList.get(i).incrementUseCount(); 136 137 // Successfully incremented. 138 i++; 139 } while (i < surfaceList.size()); 140 } catch (DeferrableSurface.SurfaceClosedException e) { 141 // Didn't successfully increment all usages, decrement those which were incremented. 142 for (i = i - 1; i >= 0; --i) { 143 surfaceList.get(i).decrementUseCount(); 144 } 145 146 // Rethrow the exception containing the surface that failed. 147 throw e; 148 } 149 } 150 } 151 152 /** 153 * Decrements the usage counts of every surface in the provided list. 154 * 155 * @param surfaceList The list of surfaces whose usage count should be decremented. 156 */ decrementAll(@onNull List<DeferrableSurface> surfaceList)157 public static void decrementAll(@NonNull List<DeferrableSurface> surfaceList) { 158 for (DeferrableSurface surface : surfaceList) { 159 surface.decrementUseCount(); 160 } 161 } 162 } 163