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