• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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  */
16 
17 package com.google.android.setupcompat.internal;
18 
19 import android.annotation.SuppressLint;
20 import android.content.Context;
21 import android.os.Bundle;
22 import android.os.RemoteException;
23 import androidx.annotation.VisibleForTesting;
24 import com.google.android.setupcompat.ISetupCompatService;
25 import com.google.android.setupcompat.logging.internal.SetupMetricsLoggingConstants.MetricType;
26 import com.google.android.setupcompat.util.Logger;
27 import java.util.concurrent.ExecutorService;
28 import java.util.concurrent.RejectedExecutionException;
29 import java.util.concurrent.TimeUnit;
30 import java.util.concurrent.TimeoutException;
31 
32 /**
33  * This class is responsible for safely executing methods on SetupCompatService. To avoid memory
34  * issues due to backed up queues, an upper bound of {@link
35  * ExecutorProvider#SETUP_METRICS_LOGGER_MAX_QUEUED} is set on the logging executor service's queue.
36  * Once the upper bound is reached, metrics published after this event are dropped silently.
37  *
38  * <p>NOTE: This class is not meant to be used directly. Please use {@link
39  * com.google.android.setupcompat.logging.SetupMetricsLogger} for publishing metric events.
40  */
41 public class SetupCompatServiceInvoker {
42 
43   private static final Logger LOG = new Logger("SetupCompatServiceInvoker");
44 
45   @SuppressLint("DefaultLocale")
logMetricEvent(@etricType int metricType, Bundle args)46   public void logMetricEvent(@MetricType int metricType, Bundle args) {
47     try {
48       loggingExecutor.execute(() -> invokeLogMetric(metricType, args));
49     } catch (RejectedExecutionException e) {
50       LOG.e(String.format("Metric of type %d dropped since queue is full.", metricType), e);
51     }
52   }
53 
54   /**
55    * Help invoke the {@link ISetupCompatService#onFocusStatusChanged} using {@code loggingExecutor}.
56    */
onFocusStatusChanged(String screenName, Bundle bundle)57   public void onFocusStatusChanged(String screenName, Bundle bundle) {
58     try {
59       loggingExecutor.execute(() -> invokeOnWindowFocusChanged(screenName, bundle));
60     } catch (RejectedExecutionException e) {
61       LOG.e(String.format("Screen %s report focus changed failed.", screenName), e);
62     }
63   }
64 
invokeLogMetric( @etricType int metricType, @SuppressWarnings("unused") Bundle args)65   private void invokeLogMetric(
66       @MetricType int metricType, @SuppressWarnings("unused") Bundle args) {
67     try {
68       ISetupCompatService setupCompatService =
69           SetupCompatServiceProvider.get(
70               context, waitTimeInMillisForServiceConnection, TimeUnit.MILLISECONDS);
71       if (setupCompatService != null) {
72         setupCompatService.logMetric(metricType, args, Bundle.EMPTY);
73       } else {
74         LOG.w("logMetric failed since service reference is null. Are the permissions valid?");
75       }
76     } catch (InterruptedException | TimeoutException | RemoteException | IllegalStateException e) {
77       LOG.e(String.format("Exception occurred while trying to log metric = [%s]", args), e);
78     }
79   }
80 
invokeOnWindowFocusChanged(String screenName, Bundle bundle)81   private void invokeOnWindowFocusChanged(String screenName, Bundle bundle) {
82     try {
83       ISetupCompatService setupCompatService =
84           SetupCompatServiceProvider.get(
85               context, waitTimeInMillisForServiceConnection, TimeUnit.MILLISECONDS);
86       if (setupCompatService != null) {
87         setupCompatService.onFocusStatusChanged(bundle);
88       } else {
89         LOG.w(
90             "Report focusChange failed since service reference is null. Are the permission valid?");
91       }
92     } catch (InterruptedException
93         | TimeoutException
94         | RemoteException
95         | UnsupportedOperationException e) {
96       LOG.e(
97           String.format(
98               "Exception occurred while %s trying report windowFocusChange to SetupWizard.",
99               screenName),
100           e);
101     }
102   }
103 
SetupCompatServiceInvoker(Context context)104   private SetupCompatServiceInvoker(Context context) {
105     this.context = context;
106     this.loggingExecutor = ExecutorProvider.setupCompatServiceInvoker.get();
107     this.waitTimeInMillisForServiceConnection = MAX_WAIT_TIME_FOR_CONNECTION_MS;
108   }
109 
110   private final Context context;
111 
112   private final ExecutorService loggingExecutor;
113   private final long waitTimeInMillisForServiceConnection;
114 
get(Context context)115   public static synchronized SetupCompatServiceInvoker get(Context context) {
116     if (instance == null) {
117       instance = new SetupCompatServiceInvoker(context.getApplicationContext());
118     }
119 
120     return instance;
121   }
122 
123   @VisibleForTesting
setInstanceForTesting(SetupCompatServiceInvoker testInstance)124   static void setInstanceForTesting(SetupCompatServiceInvoker testInstance) {
125     instance = testInstance;
126   }
127 
128   // The instance is coming from Application context which alive during the application activate and
129   // it's not depend on the activities life cycle, so we can avoid memory leak. However linter
130   // cannot distinguish Application context or activity context, so we add @SuppressLint to avoid
131   // lint error.
132   @SuppressLint("StaticFieldLeak")
133   private static SetupCompatServiceInvoker instance;
134 
135   private static final long MAX_WAIT_TIME_FOR_CONNECTION_MS = TimeUnit.SECONDS.toMillis(10);
136 }
137