Home | History | Annotate | Download | only in internal
      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 android.util.Log;
     25 import com.google.android.setupcompat.ISetupCompatService;
     26 import com.google.android.setupcompat.logging.internal.SetupMetricsLoggingConstants.MetricType;
     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  * and {@link ExecutorProvider#SETUP_COMPAT_BINDBACK_MAX_QUEUED} on the overall executor service.
     37  * Once the upper bound is reached, metrics published after this event are dropped silently.
     38  *
     39  * <p>NOTE: This class is not meant to be used directly. Please use {@link
     40  * com.google.android.setupcompat.logging.SetupMetricsLogger} for publishing metric events.
     41  */
     42 public class SetupCompatServiceInvoker {
     43 
     44   public void logMetricEvent(@MetricType int metricType, Bundle args) {
     45     try {
     46       loggingExecutor.execute(() -> invokeLogMetric(metricType, args));
     47     } catch (RejectedExecutionException e) {
     48       Log.e(TAG, String.format("Metric of type %d dropped since queue is full.", metricType), e);
     49     }
     50   }
     51 
     52   public void bindBack(String screenName, Bundle bundle) {
     53     try {
     54       setupCompatExecutor.execute(() -> invokeBindBack(screenName, bundle));
     55     } catch (RejectedExecutionException e) {
     56       Log.e(TAG, String.format("Screen %s bind back fail.", screenName), e);
     57     }
     58   }
     59 
     60   private void invokeLogMetric(
     61       @MetricType int metricType, @SuppressWarnings("unused") Bundle args) {
     62     try {
     63       ISetupCompatService setupCompatService =
     64           SetupCompatServiceProvider.get(
     65               context, waitTimeInMillisForServiceConnection, TimeUnit.MILLISECONDS);
     66       if (setupCompatService != null) {
     67         setupCompatService.logMetric(metricType, args, Bundle.EMPTY);
     68       } else {
     69         Log.w(TAG, "logMetric failed since service reference is null. Are the permissions valid?");
     70       }
     71     } catch (InterruptedException | TimeoutException | RemoteException e) {
     72       Log.e(TAG, String.format("Exception occurred while trying to log metric = [%s]", args), e);
     73     }
     74   }
     75 
     76   private void invokeBindBack(String screenName, Bundle bundle) {
     77     try {
     78       ISetupCompatService setupCompatService =
     79           SetupCompatServiceProvider.get(
     80               context, waitTimeInMillisForServiceConnection, TimeUnit.MILLISECONDS);
     81       if (setupCompatService != null) {
     82         setupCompatService.validateActivity(screenName, bundle);
     83       } else {
     84         Log.w(TAG, "BindBack failed since service reference is null. Are the permissions valid?");
     85       }
     86     } catch (InterruptedException | TimeoutException | RemoteException e) {
     87       Log.e(
     88           TAG,
     89           String.format("Exception occurred while %s trying bind back to SetupWizard.", screenName),
     90           e);
     91     }
     92   }
     93 
     94   private SetupCompatServiceInvoker(Context context) {
     95     this.context = context;
     96     this.loggingExecutor = ExecutorProvider.setupCompatServiceInvoker.get();
     97     this.setupCompatExecutor = ExecutorProvider.setupCompatExecutor.get();
     98     this.waitTimeInMillisForServiceConnection = MAX_WAIT_TIME_FOR_CONNECTION_MS;
     99   }
    100 
    101   private final Context context;
    102 
    103   private final ExecutorService loggingExecutor;
    104   private final ExecutorService setupCompatExecutor;
    105   private final long waitTimeInMillisForServiceConnection;
    106 
    107   public static synchronized SetupCompatServiceInvoker get(Context context) {
    108     if (instance == null) {
    109       instance = new SetupCompatServiceInvoker(context.getApplicationContext());
    110     }
    111 
    112     return instance;
    113   }
    114 
    115   @VisibleForTesting
    116   static void setInstanceForTesting(SetupCompatServiceInvoker testInstance) {
    117     instance = testInstance;
    118   }
    119 
    120   // The instance is coming from Application context which alive during the application activate and
    121   // it's not depend on the activities life cycle, so we can avoid memory leak. However linter
    122   // cannot distinguish Application context or activity context, so we add @SuppressLint to avoid
    123   // lint error.
    124   @SuppressLint("StaticFieldLeak")
    125   private static SetupCompatServiceInvoker instance;
    126 
    127   private static final long MAX_WAIT_TIME_FOR_CONNECTION_MS = TimeUnit.SECONDS.toMillis(10);
    128   private static final String TAG = "SucServiceInvoker";
    129 }
    130