blob: b684b0208d8d81bec3a3db03ca68a52c03b3d06f [file] [log] [blame]
Felipe Leme1dfa9a02018-10-17 17:24:37 -07001/*
2 * Copyright (C) 2018 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 */
16package android.service.intelligence;
17
Felipe Lemee348dc32018-11-05 12:35:29 -080018import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
19
20import android.annotation.CallSuper;
Felipe Lemebf93cdc2018-12-03 12:05:58 -080021import android.annotation.IntDef;
Felipe Leme1dfa9a02018-10-17 17:24:37 -070022import android.annotation.NonNull;
Felipe Lemeecb08be2018-11-27 15:48:47 -080023import android.annotation.Nullable;
Felipe Leme1dfa9a02018-10-17 17:24:37 -070024import android.annotation.SystemApi;
25import android.app.Service;
Felipe Lemeecb08be2018-11-27 15:48:47 -080026import android.content.ComponentName;
Felipe Leme1dfa9a02018-10-17 17:24:37 -070027import android.content.Intent;
Felipe Leme284ad1c2018-11-15 18:16:12 -080028import android.graphics.Rect;
29import android.os.CancellationSignal;
Felipe Lemee348dc32018-11-05 12:35:29 -080030import android.os.Handler;
31import android.os.IBinder;
32import android.os.Looper;
33import android.os.RemoteException;
Felipe Lemebf93cdc2018-12-03 12:05:58 -080034import android.os.SystemClock;
Felipe Leme284ad1c2018-11-15 18:16:12 -080035import android.service.intelligence.PresentationParams.SystemPopupPresentationParams;
36import android.util.ArrayMap;
Felipe Lemee348dc32018-11-05 12:35:29 -080037import android.util.Log;
Felipe Leme284ad1c2018-11-15 18:16:12 -080038import android.util.Pair;
Felipe Lemebf93cdc2018-12-03 12:05:58 -080039import android.util.Slog;
40import android.util.TimeUtils;
Felipe Leme284ad1c2018-11-15 18:16:12 -080041import android.view.autofill.AutofillId;
42import android.view.autofill.AutofillValue;
43import android.view.autofill.IAugmentedAutofillManagerClient;
Felipe Leme1dfa9a02018-10-17 17:24:37 -070044import android.view.intelligence.ContentCaptureEvent;
45
Felipe Leme284ad1c2018-11-15 18:16:12 -080046import com.android.internal.annotations.GuardedBy;
47
48import java.io.FileDescriptor;
49import java.io.PrintWriter;
Felipe Lemebf93cdc2018-12-03 12:05:58 -080050import java.lang.annotation.Retention;
51import java.lang.annotation.RetentionPolicy;
Felipe Leme284ad1c2018-11-15 18:16:12 -080052import java.util.ArrayList;
Felipe Leme1dfa9a02018-10-17 17:24:37 -070053import java.util.List;
Felipe Lemeecb08be2018-11-27 15:48:47 -080054import java.util.Set;
Felipe Leme1dfa9a02018-10-17 17:24:37 -070055
56/**
Felipe Lemeecb08be2018-11-27 15:48:47 -080057 * A service used to capture the content of the screen to provide contextual data in other areas of
58 * the system such as Autofill.
Felipe Leme1dfa9a02018-10-17 17:24:37 -070059 *
60 * @hide
61 */
62@SystemApi
Felipe Lemeecb08be2018-11-27 15:48:47 -080063public abstract class SmartSuggestionsService extends Service {
Felipe Leme1dfa9a02018-10-17 17:24:37 -070064
Felipe Lemeecb08be2018-11-27 15:48:47 -080065 private static final String TAG = "SmartSuggestionsService";
Felipe Lemee348dc32018-11-05 12:35:29 -080066
Felipe Leme284ad1c2018-11-15 18:16:12 -080067 // TODO(b/111330312): STOPSHIP use dynamic value, or change to false
68 static final boolean DEBUG = true;
Felipe Leme39233ff2018-11-28 16:54:23 -080069 static final boolean VERBOSE = false;
Felipe Leme284ad1c2018-11-15 18:16:12 -080070
Felipe Leme1dfa9a02018-10-17 17:24:37 -070071 /**
72 * The {@link Intent} that must be declared as handled by the service.
73 * To be supported, the service must also require the
Felipe Lemeecb08be2018-11-27 15:48:47 -080074 * {@link android.Manifest.permission#BIND_SMART_SUGGESTIONS_SERVICE} permission so
Felipe Leme1dfa9a02018-10-17 17:24:37 -070075 * that other applications can not abuse it.
76 */
77 public static final String SERVICE_INTERFACE =
Felipe Lemeecb08be2018-11-27 15:48:47 -080078 "android.service.intelligence.SmartSuggestionsService";
Felipe Leme1dfa9a02018-10-17 17:24:37 -070079
Felipe Lemee348dc32018-11-05 12:35:29 -080080 private Handler mHandler;
81
Felipe Leme284ad1c2018-11-15 18:16:12 -080082 private ArrayMap<InteractionSessionId, AutofillProxy> mAutofillProxies;
83
Felipe Lemee348dc32018-11-05 12:35:29 -080084 private final IIntelligenceService mInterface = new IIntelligenceService.Stub() {
85
86 @Override
87 public void onSessionLifecycle(InteractionContext context, InteractionSessionId sessionId)
88 throws RemoteException {
89 if (context != null) {
90 mHandler.sendMessage(
Felipe Lemeecb08be2018-11-27 15:48:47 -080091 obtainMessage(SmartSuggestionsService::onCreateInteractionSession,
92 SmartSuggestionsService.this, context, sessionId));
Felipe Lemee348dc32018-11-05 12:35:29 -080093 } else {
94 mHandler.sendMessage(
Felipe Lemeecb08be2018-11-27 15:48:47 -080095 obtainMessage(SmartSuggestionsService::onDestroyInteractionSession,
96 SmartSuggestionsService.this, sessionId));
Felipe Lemee348dc32018-11-05 12:35:29 -080097 }
98 }
Winson Chungfbbb1582018-11-13 16:09:01 -080099
Felipe Leme7a534082018-11-05 15:03:04 -0800100 @Override
Felipe Lemeecb08be2018-11-27 15:48:47 -0800101 public void onContentCaptureEventsRequest(InteractionSessionId sessionId,
102 ContentCaptureEventsRequest request) {
Felipe Leme7a534082018-11-05 15:03:04 -0800103 mHandler.sendMessage(
Felipe Lemeecb08be2018-11-27 15:48:47 -0800104 obtainMessage(SmartSuggestionsService::onContentCaptureEventsRequest,
105 SmartSuggestionsService.this, sessionId, request));
Felipe Leme7a534082018-11-05 15:03:04 -0800106
107 }
Winson Chungfbbb1582018-11-13 16:09:01 -0800108
109 @Override
110 public void onActivitySnapshot(InteractionSessionId sessionId,
111 SnapshotData snapshotData) {
112 mHandler.sendMessage(
Felipe Lemeecb08be2018-11-27 15:48:47 -0800113 obtainMessage(SmartSuggestionsService::onActivitySnapshot,
114 SmartSuggestionsService.this, sessionId, snapshotData));
Winson Chungfbbb1582018-11-13 16:09:01 -0800115 }
Felipe Leme284ad1c2018-11-15 18:16:12 -0800116
117 @Override
118 public void onAutofillRequest(InteractionSessionId sessionId, IBinder client,
Felipe Lemebf93cdc2018-12-03 12:05:58 -0800119 int autofilSessionId, AutofillId focusedId, AutofillValue focusedValue,
120 long requestTime) {
Felipe Lemeecb08be2018-11-27 15:48:47 -0800121 mHandler.sendMessage(obtainMessage(SmartSuggestionsService::handleOnAutofillRequest,
Felipe Lemebf93cdc2018-12-03 12:05:58 -0800122 SmartSuggestionsService.this, sessionId, client, autofilSessionId, focusedId,
123 focusedValue, requestTime));
Felipe Leme284ad1c2018-11-15 18:16:12 -0800124 }
125
126 @Override
127 public void onDestroyAutofillWindowsRequest(InteractionSessionId sessionId) {
128 mHandler.sendMessage(
Felipe Lemeecb08be2018-11-27 15:48:47 -0800129 obtainMessage(SmartSuggestionsService::handleOnDestroyAutofillWindowsRequest,
130 SmartSuggestionsService.this, sessionId));
Felipe Leme284ad1c2018-11-15 18:16:12 -0800131 }
Felipe Lemee348dc32018-11-05 12:35:29 -0800132 };
133
134 @CallSuper
135 @Override
136 public void onCreate() {
137 super.onCreate();
138 mHandler = new Handler(Looper.getMainLooper(), null, true);
139 }
140
141 /** @hide */
142 @Override
143 public final IBinder onBind(Intent intent) {
144 if (SERVICE_INTERFACE.equals(intent.getAction())) {
145 return mInterface.asBinder();
146 }
147 Log.w(TAG, "Tried to bind to wrong intent: " + intent);
148 return null;
149 }
150
Felipe Leme1dfa9a02018-10-17 17:24:37 -0700151 /**
Felipe Lemeecb08be2018-11-27 15:48:47 -0800152 * Explicitly limits content capture to the given packages and activities.
153 *
154 * <p>When the whitelist is set, it overrides the values passed to
155 * {@link #setActivityContentCaptureEnabled(ComponentName, boolean)}
156 * and {@link #setPackageContentCaptureEnabled(String, boolean)}.
157 *
158 * <p>To reset the whitelist, call it passing {@code null} to both arguments.
159 *
160 * <p>Useful when the service wants to restrict content capture to a category of apps, like
161 * chat apps. For example, if the service wants to support view captures on all activities of
162 * app {@code ChatApp1} and just activities {@code act1} and {@code act2} of {@code ChatApp2},
163 * it would call: {@code setContentCaptureWhitelist(Arrays.asList("ChatApp1"),
164 * Arrays.asList(new ComponentName("ChatApp2", "act1"),
165 * new ComponentName("ChatApp2", "act2")));}
166 */
167 public final void setContentCaptureWhitelist(@Nullable List<String> packages,
168 @Nullable List<ComponentName> activities) {
169 //TODO(b/111276913): implement
170 }
171
172 /**
173 * Defines whether content capture should be enabled for activities with such
174 * {@link android.content.ComponentName}.
175 *
176 * <p>Useful to blacklist a particular activity.
177 */
178 public final void setActivityContentCaptureEnabled(@NonNull ComponentName activity,
179 boolean enabled) {
180 //TODO(b/111276913): implement
181 }
182
183 /**
184 * Defines whether content capture should be enabled for activities of the app with such
185 * {@code packageName}.
186 *
187 * <p>Useful to blacklist any activity from a particular app.
188 */
189 public final void setPackageContentCaptureEnabled(@NonNull String packageName,
190 boolean enabled) {
191 //TODO(b/111276913): implement
192 }
193
194 /**
195 * Gets the activities where content capture was disabled by
196 * {@link #setActivityContentCaptureEnabled(ComponentName, boolean)}.
197 */
198 @NonNull
199 public final Set<ComponentName> getContentCaptureDisabledActivities() {
200 //TODO(b/111276913): implement
201 return null;
202 }
203
204 /**
205 * Gets the apps where content capture was disabled by
206 * {@link #setPackageContentCaptureEnabled(String, boolean)}.
207 */
208 @NonNull
209 public final Set<String> getContentCaptureDisabledPackages() {
210 //TODO(b/111276913): implement
211 return null;
212 }
213
214 /**
Felipe Leme1dfa9a02018-10-17 17:24:37 -0700215 * Creates a new interaction session.
216 *
217 * @param context interaction context
218 * @param sessionId the session's Id
219 */
220 public void onCreateInteractionSession(@NonNull InteractionContext context,
Felipe Leme39233ff2018-11-28 16:54:23 -0800221 @NonNull InteractionSessionId sessionId) {
222 if (VERBOSE) {
223 Log.v(TAG, "onCreateInteractionSession(id=" + sessionId + ", ctx=" + context + ")");
224 }
225 }
Felipe Leme1dfa9a02018-10-17 17:24:37 -0700226
227 /**
228 * Notifies the service of {@link ContentCaptureEvent events} associated with a content capture
229 * session.
230 *
231 * @param sessionId the session's Id
Felipe Lemeecb08be2018-11-27 15:48:47 -0800232 * @param request the events
Felipe Leme1dfa9a02018-10-17 17:24:37 -0700233 */
Felipe Leme284ad1c2018-11-15 18:16:12 -0800234 // TODO(b/111276913): rename to onContentCaptureEvents or something like that; also, pass a
235 // Request object so it can be extended
Felipe Lemeecb08be2018-11-27 15:48:47 -0800236 public abstract void onContentCaptureEventsRequest(@NonNull InteractionSessionId sessionId,
237 @NonNull ContentCaptureEventsRequest request);
Felipe Leme1dfa9a02018-10-17 17:24:37 -0700238
Felipe Leme284ad1c2018-11-15 18:16:12 -0800239 private void handleOnAutofillRequest(@NonNull InteractionSessionId sessionId,
Felipe Lemebf93cdc2018-12-03 12:05:58 -0800240 @NonNull IBinder client, int autofillSessionId, @NonNull AutofillId focusedId,
241 @Nullable AutofillValue focusedValue, long requestTime) {
Felipe Leme284ad1c2018-11-15 18:16:12 -0800242 if (mAutofillProxies == null) {
243 mAutofillProxies = new ArrayMap<>();
244 }
245 AutofillProxy proxy = mAutofillProxies.get(sessionId);
246 if (proxy == null) {
Felipe Lemebf93cdc2018-12-03 12:05:58 -0800247 proxy = new AutofillProxy(sessionId, client, autofillSessionId, focusedId, focusedValue,
248 requestTime);
Felipe Leme284ad1c2018-11-15 18:16:12 -0800249 mAutofillProxies.put(sessionId, proxy);
250 } else {
251 // TODO(b/111330312): figure out if it's ok to reuse the proxy; add logging
252 if (DEBUG) Log.d(TAG, "Reusing proxy for session " + sessionId);
253 }
254 // TODO(b/111330312): set cancellation signal
255 final CancellationSignal cancellationSignal = null;
256 onFillRequest(sessionId, new FillRequest(proxy), cancellationSignal,
Felipe Lemebf93cdc2018-12-03 12:05:58 -0800257 new FillController(proxy), new FillCallback(proxy));
Felipe Leme284ad1c2018-11-15 18:16:12 -0800258 }
259
260 /**
261 * Asks the service to handle an "augmented" autofill request.
262 *
263 * <p>This method is called when the "stantard" autofill service cannot handle a request, which
264 * typically occurs when:
265 * <ul>
266 * <li>Service does not recognize what should be autofilled.
267 * <li>Service does not have data to fill the request.
268 * <li>Service blacklisted that app (or activity) for autofill.
269 * <li>App disabled itself for autofill.
270 * </ul>
271 *
272 * <p>Differently from the standard autofill workflow, on augmented autofill the service is
273 * responsible to generate the autofill UI and request the Android system to autofill the
274 * activity when the user taps an action in that UI (through the
275 * {@link FillController#autofill(List)} method).
276 *
277 * <p>The service <b>MUST</b> call {@link
278 * FillCallback#onSuccess(android.service.intelligence.FillResponse)} as soon as possible,
279 * passing {@code null} when it cannot fulfill the request.
280 *
281 * @param sessionId the session's id
282 * @param request the request to handle.
283 * @param cancellationSignal signal for observing cancellation requests. The system will use
284 * this to notify you that the fill result is no longer needed and you should stop
285 * handling this fill request in order to save resources.
286 * @param controller object used to interact with the autofill system.
287 * @param callback object used to notify the result of the request. Service <b>must</b> call
288 * {@link FillCallback#onSuccess(android.service.intelligence.FillResponse)}.
289 */
290 public void onFillRequest(@NonNull InteractionSessionId sessionId, @NonNull FillRequest request,
291 @NonNull CancellationSignal cancellationSignal, @NonNull FillController controller,
292 @NonNull FillCallback callback) {
293 }
294
295 private void handleOnDestroyAutofillWindowsRequest(@NonNull InteractionSessionId sessionId) {
296 AutofillProxy proxy = null;
297 if (mAutofillProxies != null) {
298 proxy = mAutofillProxies.get(sessionId);
299 }
300 if (proxy == null) {
301 // TODO(b/111330312): this might be fine, in which case we should logv it
302 Log.w(TAG, "No proxy for session " + sessionId);
303 return;
304 }
305 proxy.destroy();
306 mAutofillProxies.remove(sessionId);
307 }
308
309 @Override
310 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
311 if (mAutofillProxies != null) {
312 final int size = mAutofillProxies.size();
313 pw.print("Number proxies: "); pw.println(size);
314 for (int i = 0; i < size; i++) {
315 final InteractionSessionId sessionId = mAutofillProxies.keyAt(i);
316 final AutofillProxy proxy = mAutofillProxies.valueAt(i);
317 pw.print(i); pw.print(") SessionId="); pw.print(sessionId); pw.println(":");
318 proxy.dump(" ", pw);
319 }
320 }
321 }
322
Felipe Leme1dfa9a02018-10-17 17:24:37 -0700323 /**
Felipe Lemeecb08be2018-11-27 15:48:47 -0800324 * Notifies the service of {@link SnapshotData snapshot data} associated with a session.
Winson Chungfbbb1582018-11-13 16:09:01 -0800325 *
326 * @param sessionId the session's Id
327 * @param snapshotData the data
328 */
329 public void onActivitySnapshot(@NonNull InteractionSessionId sessionId,
330 @NonNull SnapshotData snapshotData) {}
331
332 /**
Felipe Lemee348dc32018-11-05 12:35:29 -0800333 * Destroys the interaction session.
Felipe Leme1dfa9a02018-10-17 17:24:37 -0700334 *
335 * @param sessionId the id of the session to destroy
336 */
Felipe Leme39233ff2018-11-28 16:54:23 -0800337 public void onDestroyInteractionSession(@NonNull InteractionSessionId sessionId) {
338 if (VERBOSE) {
339 Log.v(TAG, "onDestroyInteractionSession(id=" + sessionId + ")");
340 }
341 }
Felipe Leme284ad1c2018-11-15 18:16:12 -0800342
343 /** @hide */
344 static final class AutofillProxy {
Felipe Lemebf93cdc2018-12-03 12:05:58 -0800345
346 static final int REPORT_EVENT_ON_SUCCESS = 1;
347 static final int REPORT_EVENT_UI_SHOWN = 2;
348 static final int REPORT_EVENT_UI_DESTROYED = 3;
349
350 @IntDef(prefix = { "REPORT_EVENT_" }, value = {
351 REPORT_EVENT_ON_SUCCESS,
352 REPORT_EVENT_UI_SHOWN,
353 REPORT_EVENT_UI_DESTROYED
354 })
355 @Retention(RetentionPolicy.SOURCE)
356 @interface ReportEvent{}
357
358
Felipe Leme284ad1c2018-11-15 18:16:12 -0800359 private final Object mLock = new Object();
360 private final IAugmentedAutofillManagerClient mClient;
361 private final int mAutofillSessionId;
362 public final InteractionSessionId sessionId;
363 public final AutofillId focusedId;
Felipe Lemebf93cdc2018-12-03 12:05:58 -0800364 public final AutofillValue focusedValue;
365
366 // Objects used to log metrics
367 private final long mRequestTime;
368 private long mOnSuccessTime;
369 private long mUiFirstShownTime;
370 private long mUiFirstDestroyedTime;
Felipe Leme284ad1c2018-11-15 18:16:12 -0800371
372 @GuardedBy("mLock")
373 private SystemPopupPresentationParams mSmartSuggestion;
374
375 @GuardedBy("mLock")
376 private FillWindow mFillWindow;
377
378 private AutofillProxy(@NonNull InteractionSessionId sessionId, @NonNull IBinder client,
Felipe Lemebf93cdc2018-12-03 12:05:58 -0800379 int autofillSessionId, @NonNull AutofillId focusedId,
380 @Nullable AutofillValue focusedValue, long requestTime) {
Felipe Leme284ad1c2018-11-15 18:16:12 -0800381 this.sessionId = sessionId;
382 mClient = IAugmentedAutofillManagerClient.Stub.asInterface(client);
383 mAutofillSessionId = autofillSessionId;
384 this.focusedId = focusedId;
Felipe Lemebf93cdc2018-12-03 12:05:58 -0800385 this.focusedValue = focusedValue;
386 this.mRequestTime = requestTime;
Felipe Leme284ad1c2018-11-15 18:16:12 -0800387 // TODO(b/111330312): linkToDeath
388 }
389
390 @NonNull
391 public SystemPopupPresentationParams getSmartSuggestionParams() {
392 synchronized (mLock) {
393 if (mSmartSuggestion != null) {
394 return mSmartSuggestion;
395 }
396 Rect rect;
397 try {
398 rect = mClient.getViewCoordinates(focusedId);
399 } catch (RemoteException e) {
400 Log.w(TAG, "Could not get coordinates for " + focusedId);
401 return null;
402 }
403 if (rect == null) {
404 if (DEBUG) Log.d(TAG, "getViewCoordinates(" + focusedId + ") returned null");
405 return null;
406 }
407 mSmartSuggestion = new SystemPopupPresentationParams(this, rect);
408 return mSmartSuggestion;
409 }
410 }
411
412 public void autofill(@NonNull List<Pair<AutofillId, AutofillValue>> pairs)
413 throws RemoteException {
414 final int size = pairs.size();
415 final List<AutofillId> ids = new ArrayList<>(size);
416 final List<AutofillValue> values = new ArrayList<>(size);
417 for (int i = 0; i < size; i++) {
418 final Pair<AutofillId, AutofillValue> pair = pairs.get(i);
419 ids.add(pair.first);
420 values.add(pair.second);
421 }
422 mClient.autofill(mAutofillSessionId, ids, values);
423 }
424
425 public void setFillWindow(@NonNull FillWindow fillWindow) {
426 synchronized (mLock) {
427 mFillWindow = fillWindow;
428 }
429 }
430
431 public FillWindow getFillWindow() {
432 synchronized (mLock) {
433 return mFillWindow;
434 }
435 }
436
Felipe Lemebf93cdc2018-12-03 12:05:58 -0800437 // Used for metrics.
438 public void report(@ReportEvent int event) {
439 switch (event) {
440 case REPORT_EVENT_ON_SUCCESS:
441 if (mOnSuccessTime == 0) {
442 mOnSuccessTime = SystemClock.elapsedRealtime();
443 if (DEBUG) {
444 Slog.d(TAG, "Service responsed in "
445 + TimeUtils.formatDuration(mOnSuccessTime - mRequestTime));
446 }
447 }
448 break;
449 case REPORT_EVENT_UI_SHOWN:
450 if (mUiFirstShownTime == 0) {
451 mUiFirstShownTime = SystemClock.elapsedRealtime();
452 if (DEBUG) {
453 Slog.d(TAG, "UI shown in "
454 + TimeUtils.formatDuration(mUiFirstShownTime - mRequestTime));
455 }
456 }
457 break;
458 case REPORT_EVENT_UI_DESTROYED:
459 if (mUiFirstDestroyedTime == 0) {
460 mUiFirstDestroyedTime = SystemClock.elapsedRealtime();
461 if (DEBUG) {
462 Slog.d(TAG, "UI destroyed in "
463 + TimeUtils.formatDuration(
464 mUiFirstDestroyedTime - mRequestTime));
465 }
466 }
467 break;
468 default:
469 Slog.w(TAG, "invalid event reported: " + event);
470 }
471 // TODO(b/111330312): log metrics as well
472 }
473
474
Felipe Leme284ad1c2018-11-15 18:16:12 -0800475 public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
476 pw.print(prefix); pw.print("afSessionId: "); pw.println(mAutofillSessionId);
477 pw.print(prefix); pw.print("focusedId: "); pw.println(focusedId);
Felipe Lemebf93cdc2018-12-03 12:05:58 -0800478 if (focusedValue != null) {
479 pw.print(prefix); pw.print("focusedValue: "); pw.println(focusedValue);
480 }
Felipe Leme284ad1c2018-11-15 18:16:12 -0800481 pw.print(prefix); pw.print("client: "); pw.println(mClient);
482 final String prefix2 = prefix + " ";
483 if (mFillWindow != null) {
484 pw.print(prefix); pw.println("window:");
485 mFillWindow.dump(prefix2, pw);
486 }
487 if (mSmartSuggestion != null) {
488 pw.print(prefix); pw.println("smartSuggestion:");
489 mSmartSuggestion.dump(prefix2, pw);
490 }
Felipe Lemebf93cdc2018-12-03 12:05:58 -0800491 if (mOnSuccessTime > 0) {
492 final long responseTime = mOnSuccessTime - mRequestTime;
493 pw.print(prefix); pw.print("response time: ");
494 TimeUtils.formatDuration(responseTime, pw); pw.println();
495 }
496
497 if (mUiFirstShownTime > 0) {
498 final long uiRenderingTime = mUiFirstShownTime - mRequestTime;
499 pw.print(prefix); pw.print("UI rendering time: ");
500 TimeUtils.formatDuration(uiRenderingTime, pw); pw.println();
501 }
502
503 if (mUiFirstDestroyedTime > 0) {
504 final long uiTotalTime = mUiFirstDestroyedTime - mRequestTime;
505 pw.print(prefix); pw.print("UI life time: ");
506 TimeUtils.formatDuration(uiTotalTime, pw); pw.println();
507 }
Felipe Leme284ad1c2018-11-15 18:16:12 -0800508 }
509
510 private void destroy() {
511 synchronized (mLock) {
512 if (mFillWindow != null) {
513 if (DEBUG) Log.d(TAG, "destroying window");
514 mFillWindow.destroy();
515 }
516 }
517 }
518 }
Felipe Leme1dfa9a02018-10-17 17:24:37 -0700519}