Suppress RescueParty when active USB connection.

When there is a very early system server runtime restart, we may not
yet have a published BatteryManagerInternal, so we need to go directly
to the "batteryproperties" native service to detect the USB state.

Test: builds, rescue is suppressed when USB is connected
Bug: 34872406
Change-Id: I949984cb95495c77de85ac322075177cff07b8b6
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index c77a407..33351ff 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -20,9 +20,12 @@
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.os.BatteryManager;
-import android.os.BatteryManagerInternal;
+import android.os.BatteryProperties;
 import android.os.Build;
+import android.os.IBatteryPropertiesListener;
+import android.os.IBatteryPropertiesRegistrar;
 import android.os.RecoverySystem;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -31,11 +34,15 @@
 import android.text.format.DateUtils;
 import android.util.ExceptionUtils;
 import android.util.MathUtils;
+import android.util.MutableBoolean;
 import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.util.ArrayUtils;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 /**
  * Utilities to help rescue the system from crash loops. Callers are expected to
  * report boot events and persistent app crashes, and if they happen frequently
@@ -66,24 +73,26 @@
 
     private static boolean isDisabled() {
         // We're disabled on all engineering devices
-        if (Build.IS_ENG) return true;
+        if (Build.IS_ENG) {
+            Slog.v(TAG, "Disabled because of eng build");
+            return true;
+        }
 
         // We're disabled on userdebug devices connected over USB, since that's
         // a decent signal that someone is actively trying to debug the device,
         // or that it's in a lab environment.
-        if (Build.IS_USERDEBUG) {
-            try {
-                if (LocalServices.getService(BatteryManagerInternal.class)
-                        .getPlugType() == BatteryManager.BATTERY_PLUGGED_USB) {
-                    return true;
-                } else {
-                }
-            } catch (Throwable ignored) {
-            }
+        if (Build.IS_USERDEBUG && isUsbActive()) {
+            Slog.v(TAG, "Disabled because of active USB connection");
+            return true;
         }
 
         // One last-ditch check
-        return SystemProperties.getBoolean(PROP_DISABLE_RESCUE, false);
+        if (SystemProperties.getBoolean(PROP_DISABLE_RESCUE, false)) {
+            Slog.v(TAG, "Disabled because of manual property");
+            return true;
+        }
+
+        return false;
     }
 
     /**
@@ -185,14 +194,14 @@
         final ContentResolver resolver = context.getContentResolver();
         try {
             Settings.Global.resetToDefaultsAsUser(resolver, null, mode, UserHandle.USER_SYSTEM);
-        } catch (Exception e) {
-            res = new RuntimeException("Failed to reset global settings", e);
+        } catch (Throwable t) {
+            res = new RuntimeException("Failed to reset global settings", t);
         }
         for (int userId : getAllUserIds(context)) {
             try {
                 Settings.Secure.resetToDefaultsAsUser(resolver, null, mode, userId);
-            } catch (Exception e) {
-                res = new RuntimeException("Failed to reset secure settings for " + userId, e);
+            } catch (Throwable t) {
+                res = new RuntimeException("Failed to reset secure settings for " + userId, t);
             }
         }
         if (res != null) {
@@ -314,6 +323,38 @@
         return userIds;
     }
 
+    /**
+     * Hacky test to check if the device has an active USB connection, which is
+     * a good proxy for someone doing local development work. It uses a low
+     * level call since we may not have started {@link BatteryManager} yet.
+     */
+    private static boolean isUsbActive() {
+        final MutableBoolean res = new MutableBoolean(false);
+        final CountDownLatch latch = new CountDownLatch(1);
+        final IBatteryPropertiesListener listener = new IBatteryPropertiesListener.Stub() {
+            @Override
+            public void batteryPropertiesChanged(BatteryProperties props) {
+                res.value = props.chargerUsbOnline;
+                latch.countDown();
+            }
+        };
+
+        try {
+            final IBatteryPropertiesRegistrar bpr = IBatteryPropertiesRegistrar.Stub
+                    .asInterface(ServiceManager.getService("batteryproperties"));
+            bpr.registerListener(listener);
+            try {
+                latch.await(5, TimeUnit.SECONDS);
+            } finally {
+                bpr.unregisterListener(listener);
+            }
+            return res.value;
+        } catch (Throwable t) {
+            Slog.w(TAG, "Failed to determine if device was on USB", t);
+            return false;
+        }
+    }
+
     private static String levelToString(int level) {
         switch (level) {
             case LEVEL_NONE: return "NONE";