1 // Copyright 2015 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.base; 6 7 import android.content.Context; 8 import android.provider.Settings; 9 10 import androidx.annotation.Nullable; 11 12 import org.chromium.base.supplier.Supplier; 13 14 import java.io.File; 15 16 /** Provides implementation of command line initialization for Android. */ 17 public final class CommandLineInitUtil { 18 /** 19 * The location of the command line file needs to be in a protected 20 * directory so requires root access to be tweaked, i.e., no other app in a 21 * regular (non-rooted) device can change this file's contents. 22 * See below for debugging on a regular (non-rooted) device. 23 */ 24 private static final String COMMAND_LINE_FILE_PATH = "/data/local"; 25 26 /** 27 * This path (writable by the shell in regular non-rooted "user" builds) is used when: 28 * 1) The "debug app" is set to the application calling this. 29 * and 30 * 2) ADB is enabled. 31 * 3) Force enabled by the embedder. 32 */ 33 private static final String COMMAND_LINE_FILE_PATH_DEBUG_APP = "/data/local/tmp"; 34 35 /** The name of the command line file to pull arguments from. */ 36 private static String sFilenameOverrideForTesting; 37 CommandLineInitUtil()38 private CommandLineInitUtil() {} 39 40 /** Set the filename to use. */ setFilenameOverrideForTesting(String value)41 public static void setFilenameOverrideForTesting(String value) { 42 sFilenameOverrideForTesting = value; 43 } 44 45 /** 46 * Initializes the CommandLine class, pulling command line arguments from {@code fileName}. 47 * @param fileName The name of the command line file to pull arguments from. 48 */ initCommandLine(String fileName)49 public static void initCommandLine(String fileName) { 50 initCommandLine(fileName, null); 51 } 52 53 /** 54 * Initializes the CommandLine class, pulling command line arguments from {@code fileName}. 55 * @param fileName The name of the command line file to pull arguments from. 56 * @param shouldUseDebugFlags If non-null, returns whether debug flags are allowed to be used. 57 */ initCommandLine( String fileName, @Nullable Supplier<Boolean> shouldUseDebugFlags)58 public static void initCommandLine( 59 String fileName, @Nullable Supplier<Boolean> shouldUseDebugFlags) { 60 if (sFilenameOverrideForTesting != null) { 61 fileName = sFilenameOverrideForTesting; 62 } 63 assert !CommandLine.isInitialized(); 64 File commandLineFile = new File(COMMAND_LINE_FILE_PATH_DEBUG_APP, fileName); 65 // shouldUseDebugCommandLine() uses IPC, so don't bother calling it if no flags file exists. 66 boolean debugFlagsExist = commandLineFile.exists(); 67 if (!debugFlagsExist || !shouldUseDebugCommandLine(shouldUseDebugFlags)) { 68 commandLineFile = new File(COMMAND_LINE_FILE_PATH, fileName); 69 } 70 CommandLine.initFromFile(commandLineFile.getPath()); 71 } 72 73 /** 74 * Use an alternative path if: 75 * - The current build is "eng" or "userdebug", OR 76 * - adb is enabled and this is the debug app, OR 77 * - Force enabled by the embedder. 78 * @param shouldUseDebugFlags If non-null, returns whether debug flags are allowed to be used. 79 */ shouldUseDebugCommandLine( @ullable Supplier<Boolean> shouldUseDebugFlags)80 private static boolean shouldUseDebugCommandLine( 81 @Nullable Supplier<Boolean> shouldUseDebugFlags) { 82 if (shouldUseDebugFlags != null && shouldUseDebugFlags.get()) return true; 83 Context context = ContextUtils.getApplicationContext(); 84 // Check isDebugAndroid() last to get full code coverage when using userdebug devices. 85 return context.getPackageName().equals(getDebugApp(context)) || BuildInfo.isDebugAndroid(); 86 } 87 getDebugApp(Context context)88 private static String getDebugApp(Context context) { 89 boolean adbEnabled = 90 Settings.Global.getInt(context.getContentResolver(), Settings.Global.ADB_ENABLED, 0) 91 == 1; 92 if (adbEnabled) { 93 return Settings.Global.getString( 94 context.getContentResolver(), Settings.Global.DEBUG_APP); 95 } 96 return null; 97 } 98 } 99