1 // Copyright 2022 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.net.impl; 6 7 import android.content.Context; 8 import android.os.Build; 9 import android.util.Log; // TODO(crbug/1394709): use org.chromium.base.Log instead 10 11 import androidx.annotation.Nullable; 12 import androidx.annotation.VisibleForTesting; 13 14 import org.chromium.net.impl.CronetLogger.CronetSource; 15 16 /** 17 * Takes care of instantiating the correct CronetLogger. 18 */ 19 public final class CronetLoggerFactory { 20 private static final String TAG = CronetLoggerFactory.class.getSimpleName(); 21 private static final int SAMPLE_RATE_PER_SECOND = 1; 22 CronetLoggerFactory()23 private CronetLoggerFactory() {} 24 25 private static final CronetLogger sDefaultLogger = new NoOpLogger(); 26 private static CronetLogger sTestingLogger; 27 28 // Class that is packaged for Cronet telemetry. 29 private static final String CRONET_LOGGER_IMPL_CLASS = 30 "com.google.net.cronet.telemetry.CronetLoggerImpl"; 31 32 /** 33 * Bypasses CronetLoggerFactory logic and always creates a NoOpLogger. 34 * To be used only as a kill-switch for logging. 35 * @return a NoOpLogger instance. 36 */ createNoOpLogger()37 public static CronetLogger createNoOpLogger() { 38 return sDefaultLogger; 39 } 40 41 /** 42 * @return The correct CronetLogger to be used for logging. 43 */ createLogger(Context ctx, CronetSource source)44 public static CronetLogger createLogger(Context ctx, CronetSource source) { 45 if (sTestingLogger != null) return sTestingLogger; 46 47 // The CronetLoggerImpl only works from apiLevel 30 48 if (!CronetManifest.isAppOptedInForTelemetry(ctx, source) 49 || Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { 50 return sDefaultLogger; 51 } 52 53 Class<? extends CronetLogger> cronetLoggerImplClass = fetchLoggerImplClass(); 54 if (cronetLoggerImplClass == null) return sDefaultLogger; 55 56 try { 57 return cronetLoggerImplClass.getConstructor(int.class).newInstance( 58 SAMPLE_RATE_PER_SECOND); 59 } catch (Exception e) { 60 // Pass - since we dont want any failure, catch any exception that might arise. 61 Log.e(TAG, "Exception creating an instance of CronetLoggerImpl", e); 62 } 63 return sDefaultLogger; 64 } 65 66 @VisibleForTesting setLoggerForTesting(@ullable CronetLogger testingLogger)67 public static void setLoggerForTesting(@Nullable CronetLogger testingLogger) { 68 sTestingLogger = testingLogger; 69 } 70 fetchLoggerImplClass()71 private static Class<? extends CronetLogger> fetchLoggerImplClass() { 72 ClassLoader loader = CronetLoggerFactory.class.getClassLoader(); 73 try { 74 return loader.loadClass(CRONET_LOGGER_IMPL_CLASS).asSubclass(CronetLogger.class); 75 } catch (Exception e) { // catching all exceptions since we don't want to crash the client 76 Log.e(TAG, "Exception fetching LoggerImpl class", e); 77 return null; 78 } 79 } 80 } 81