1 /* 2 * Copyright (C) 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 com.android.systemui.screenshot; 18 19 import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS; 20 import static com.android.systemui.screenshot.LogConfig.logTag; 21 import static com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType; 22 23 import android.app.ActivityManager; 24 import android.app.Notification; 25 import android.content.ComponentName; 26 import android.content.Intent; 27 import android.graphics.Bitmap; 28 import android.net.Uri; 29 import android.os.SystemClock; 30 import android.os.UserHandle; 31 import android.util.Log; 32 33 import com.android.internal.annotations.VisibleForTesting; 34 import com.android.systemui.dagger.SysUISingleton; 35 import com.android.systemui.shared.system.ActivityManagerWrapper; 36 37 import java.util.Collections; 38 import java.util.List; 39 import java.util.concurrent.CompletableFuture; 40 import java.util.concurrent.TimeUnit; 41 import java.util.concurrent.TimeoutException; 42 43 import javax.inject.Inject; 44 import javax.inject.Provider; 45 46 /** 47 * Collects the static functions for retrieving and acting on smart actions. 48 */ 49 @SysUISingleton 50 public class ScreenshotSmartActions { 51 private static final String TAG = logTag(ScreenshotSmartActions.class); 52 private final Provider<ScreenshotNotificationSmartActionsProvider> 53 mScreenshotNotificationSmartActionsProviderProvider; 54 55 @Inject ScreenshotSmartActions( Provider<ScreenshotNotificationSmartActionsProvider> screenshotNotificationSmartActionsProviderProvider )56 public ScreenshotSmartActions( 57 Provider<ScreenshotNotificationSmartActionsProvider> 58 screenshotNotificationSmartActionsProviderProvider 59 ) { 60 mScreenshotNotificationSmartActionsProviderProvider = 61 screenshotNotificationSmartActionsProviderProvider; 62 } 63 64 @VisibleForTesting getSmartActionsFuture( String screenshotId, Uri screenshotUri, Bitmap image, ScreenshotNotificationSmartActionsProvider smartActionsProvider, ScreenshotSmartActionType actionType, boolean smartActionsEnabled, UserHandle userHandle)65 CompletableFuture<List<Notification.Action>> getSmartActionsFuture( 66 String screenshotId, Uri screenshotUri, Bitmap image, 67 ScreenshotNotificationSmartActionsProvider smartActionsProvider, 68 ScreenshotSmartActionType actionType, 69 boolean smartActionsEnabled, UserHandle userHandle) { 70 if (DEBUG_ACTIONS) { 71 Log.d(TAG, String.format( 72 "getSmartActionsFuture id=%s, uri=%s, provider=%s, actionType=%s, " 73 + "smartActionsEnabled=%b, userHandle=%s", 74 screenshotId, screenshotUri, smartActionsProvider.getClass(), actionType, 75 smartActionsEnabled, userHandle)); 76 } 77 if (!smartActionsEnabled) { 78 if (DEBUG_ACTIONS) { 79 Log.d(TAG, "Screenshot Intelligence not enabled, returning empty list."); 80 } 81 return CompletableFuture.completedFuture(Collections.emptyList()); 82 } 83 if (image.getConfig() != Bitmap.Config.HARDWARE) { 84 if (DEBUG_ACTIONS) { 85 Log.d(TAG, String.format("Bitmap expected: Hardware, Bitmap found: %s. " 86 + "Returning empty list.", image.getConfig())); 87 } 88 return CompletableFuture.completedFuture(Collections.emptyList()); 89 } 90 CompletableFuture<List<Notification.Action>> smartActionsFuture; 91 long startTimeMs = SystemClock.uptimeMillis(); 92 try { 93 ActivityManager.RunningTaskInfo runningTask = 94 ActivityManagerWrapper.getInstance().getRunningTask(); 95 ComponentName componentName = 96 (runningTask != null && runningTask.topActivity != null) 97 ? runningTask.topActivity 98 : new ComponentName("", ""); 99 smartActionsFuture = smartActionsProvider.getActions(screenshotId, screenshotUri, image, 100 componentName, actionType, userHandle); 101 } catch (Throwable e) { 102 long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs; 103 smartActionsFuture = CompletableFuture.completedFuture(Collections.emptyList()); 104 if (DEBUG_ACTIONS) { 105 Log.e(TAG, "Failed to get future for screenshot notification smart actions.", e); 106 } 107 notifyScreenshotOp(screenshotId, smartActionsProvider, 108 ScreenshotNotificationSmartActionsProvider.ScreenshotOp.REQUEST_SMART_ACTIONS, 109 ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.ERROR, 110 waitTimeMs); 111 } 112 return smartActionsFuture; 113 } 114 115 @VisibleForTesting getSmartActions(String screenshotId, CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs, ScreenshotNotificationSmartActionsProvider smartActionsProvider, ScreenshotSmartActionType actionType)116 List<Notification.Action> getSmartActions(String screenshotId, 117 CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs, 118 ScreenshotNotificationSmartActionsProvider smartActionsProvider, 119 ScreenshotSmartActionType actionType) { 120 long startTimeMs = SystemClock.uptimeMillis(); 121 if (DEBUG_ACTIONS) { 122 Log.d(TAG, 123 String.format("getSmartActions id=%s, timeoutMs=%d, actionType=%s, provider=%s", 124 screenshotId, timeoutMs, actionType, smartActionsProvider.getClass())); 125 } 126 try { 127 List<Notification.Action> actions = smartActionsFuture.get(timeoutMs, 128 TimeUnit.MILLISECONDS); 129 long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs; 130 if (DEBUG_ACTIONS) { 131 Log.d(TAG, String.format("Got %d smart actions. Wait time: %d ms, actionType=%s", 132 actions.size(), waitTimeMs, actionType)); 133 } 134 notifyScreenshotOp(screenshotId, smartActionsProvider, 135 ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS, 136 ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.SUCCESS, 137 waitTimeMs); 138 return actions; 139 } catch (Throwable e) { 140 long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs; 141 if (DEBUG_ACTIONS) { 142 Log.e(TAG, String.format( 143 "Error getting smart actions. Wait time: %d ms, actionType=%s", 144 waitTimeMs, actionType), e); 145 } 146 ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status = 147 (e instanceof TimeoutException) 148 ? ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.TIMEOUT 149 : ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.ERROR; 150 notifyScreenshotOp(screenshotId, smartActionsProvider, 151 ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS, 152 status, waitTimeMs); 153 return Collections.emptyList(); 154 } 155 } 156 notifyScreenshotOp(String screenshotId, ScreenshotNotificationSmartActionsProvider smartActionsProvider, ScreenshotNotificationSmartActionsProvider.ScreenshotOp op, ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status, long durationMs)157 void notifyScreenshotOp(String screenshotId, 158 ScreenshotNotificationSmartActionsProvider smartActionsProvider, 159 ScreenshotNotificationSmartActionsProvider.ScreenshotOp op, 160 ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status, long durationMs) { 161 if (DEBUG_ACTIONS) { 162 Log.d(TAG, String.format("%s notifyOp: %s id=%s, status=%s, durationMs=%d", 163 smartActionsProvider.getClass(), op, screenshotId, status, durationMs)); 164 } 165 try { 166 smartActionsProvider.notifyOp(screenshotId, op, status, durationMs); 167 } catch (Throwable e) { 168 Log.e(TAG, "Error in notifyScreenshotOp: ", e); 169 } 170 } 171 notifyScreenshotAction(String screenshotId, String action, boolean isSmartAction, Intent intent)172 void notifyScreenshotAction(String screenshotId, String action, 173 boolean isSmartAction, Intent intent) { 174 try { 175 ScreenshotNotificationSmartActionsProvider provider = 176 mScreenshotNotificationSmartActionsProviderProvider.get(); 177 if (DEBUG_ACTIONS) { 178 Log.d(TAG, String.format("%s notifyAction: %s id=%s, isSmartAction=%b", 179 provider.getClass(), action, screenshotId, isSmartAction)); 180 } 181 provider.notifyAction(screenshotId, action, isSmartAction, intent); 182 } catch (Throwable e) { 183 Log.e(TAG, "Error in notifyScreenshotAction: ", e); 184 } 185 } 186 } 187