Merge changes from topic "anomaly_system_app" into pi-dev

* changes:
  Add method to check if app has launcher activity
  Add system app check for anomaly detection.
diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java
index e8668d1..111b279 100644
--- a/src/com/android/settings/fuelgauge/BatteryUtils.java
+++ b/src/com/android/settings/fuelgauge/BatteryUtils.java
@@ -21,17 +21,20 @@
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.os.BatteryManager;
+import android.content.pm.ResolveInfo;
 import android.os.BatteryStats;
 import android.os.Bundle;
 import android.os.Build;
+import android.os.Process;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 import android.support.annotation.StringRes;
 import android.support.annotation.VisibleForTesting;
 import android.support.annotation.WorkerThread;
+import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.Log;
 import android.util.SparseLongArray;
@@ -43,6 +46,7 @@
 import com.android.settings.fuelgauge.anomaly.Anomaly;
 import com.android.settings.overlay.FeatureFactory;
 
+import com.android.settingslib.fuelgauge.PowerWhitelistBackend;
 import com.android.settingslib.utils.PowerUtil;
 
 import java.lang.annotation.Retention;
@@ -531,5 +535,65 @@
 
         return false;
     }
+
+    /**
+     * Return {@code true} if we should hide anomaly app represented by {@code uid}
+     */
+    public boolean shouldHideAnomaly(PowerWhitelistBackend powerWhitelistBackend, int uid) {
+        final String[] packageNames = mPackageManager.getPackagesForUid(uid);
+        if (ArrayUtils.isEmpty(packageNames)) {
+            // Don't show it if app has been uninstalled
+            return true;
+        }
+
+        return isSystemUid(uid) || powerWhitelistBackend.isSysWhitelistedExceptIdle(packageNames)
+                || (isSystemApp(mPackageManager, packageNames) && !hasLauncherEntry(packageNames));
+    }
+
+    private boolean isSystemUid(int uid) {
+        final int appUid = UserHandle.getAppId(uid);
+        return appUid >= Process.ROOT_UID && appUid < Process.FIRST_APPLICATION_UID;
+    }
+
+    private boolean isSystemApp(PackageManager packageManager, String[] packageNames) {
+        for (String packageName : packageNames) {
+            try {
+                final ApplicationInfo info = packageManager.getApplicationInfo(packageName,
+                        0 /* flags */);
+                if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                    return true;
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.e(TAG, "Package not found: " + packageName, e);
+            }
+        }
+
+        return false;
+    }
+
+    private boolean hasLauncherEntry(String[] packageNames) {
+        final Intent launchIntent = new Intent(Intent.ACTION_MAIN, null);
+        launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+        // If we do not specify MATCH_DIRECT_BOOT_AWARE or
+        // MATCH_DIRECT_BOOT_UNAWARE, system will derive and update the flags
+        // according to the user's lock state. When the user is locked,
+        // components
+        // with ComponentInfo#directBootAware == false will be filtered. We should
+        // explicitly include both direct boot aware and unaware components here.
+        final List<ResolveInfo> resolveInfos = mPackageManager.queryIntentActivities(launchIntent,
+                PackageManager.MATCH_DISABLED_COMPONENTS
+                        | PackageManager.MATCH_DIRECT_BOOT_AWARE
+                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                        | PackageManager.MATCH_SYSTEM_ONLY);
+        for (int i = 0, size = resolveInfos.size(); i < size; i++) {
+            final ResolveInfo resolveInfo = resolveInfos.get(i);
+            if (ArrayUtils.contains(packageNames, resolveInfo.activityInfo.packageName)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
 }
 
diff --git a/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java b/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java
index 8928efd..661cb53 100644
--- a/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java
+++ b/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobService.java
@@ -30,6 +30,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.Process;
@@ -145,21 +146,24 @@
                     : Settings.Global.getInt(contentResolver,
                             Settings.Global.APP_AUTO_RESTRICTION_ENABLED, ON) == ON;
             final String packageName = batteryUtils.getPackageName(uid);
-            if (uid != UID_NULL && !isSystemUid(uid)
-                    && !powerWhitelistBackend.isSysWhitelistedExceptIdle(
-                    packageManager.getPackagesForUid(uid))) {
-                boolean anomalyDetected = true;
-                if (anomalyInfo.anomalyType
-                        == StatsManagerConfig.AnomalyType.EXCESSIVE_BACKGROUND_SERVICE) {
-                    if (!batteryUtils.isPreOApp(packageName)
-                            || !batteryUtils.isAppHeavilyUsed(batteryStatsHelper, userManager, uid,
-                            policy.excessiveBgDrainPercentage)) {
-                        // Don't report if it is not legacy app or haven't used much battery
-                        anomalyDetected = false;
-                    }
-                }
 
-                if (anomalyDetected) {
+            final boolean anomalyDetected;
+            if (isExcessiveBackgroundAnomaly(anomalyInfo)) {
+                anomalyDetected = batteryUtils.isPreOApp(packageName)
+                        && batteryUtils.isAppHeavilyUsed(batteryStatsHelper, userManager, uid,
+                        policy.excessiveBgDrainPercentage);
+            } else {
+                anomalyDetected = true;
+            }
+
+            if (anomalyDetected) {
+                if (batteryUtils.shouldHideAnomaly(powerWhitelistBackend, uid)) {
+                    metricsFeatureProvider.action(context,
+                            MetricsProto.MetricsEvent.ACTION_ANOMALY_IGNORED,
+                            packageName,
+                            Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT,
+                                    anomalyInfo.anomalyType));
+                } else {
                     if (autoFeatureOn && anomalyInfo.autoRestriction) {
                         // Auto restrict this app
                         batteryUtils.setForceAppStandby(uid, packageName,
@@ -215,8 +219,8 @@
         return UID_NULL;
     }
 
-    private boolean isSystemUid(int uid) {
-        final int appUid = UserHandle.getAppId(uid);
-        return appUid >= Process.ROOT_UID && appUid < Process.FIRST_APPLICATION_UID;
+    private boolean isExcessiveBackgroundAnomaly(AnomalyInfo anomalyInfo) {
+        return anomalyInfo.anomalyType
+                == StatsManagerConfig.AnomalyType.EXCESSIVE_BACKGROUND_SERVICE;
     }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
index e05ff52..772bb8d 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
@@ -37,8 +37,10 @@
 
 import android.app.AppOpsManager;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.os.BatteryStats;
 import android.os.Build;
 import android.os.Bundle;
@@ -53,6 +55,7 @@
 import com.android.settings.fuelgauge.anomaly.Anomaly;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.fuelgauge.PowerWhitelistBackend;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -81,7 +84,7 @@
     private static final long TIME_SINCE_LAST_FULL_CHARGE_US =
             TIME_SINCE_LAST_FULL_CHARGE_MS * 1000;
 
-    private static final int UID = 123;
+    private static final int UID = 12345;
     private static final long TIME_EXPECTED_FOREGROUND = 1500;
     private static final long TIME_EXPECTED_BACKGROUND = 6000;
     private static final long TIME_EXPECTED_ALL = 7500;
@@ -141,6 +144,8 @@
     private ApplicationInfo mHighApplicationInfo;
     @Mock
     private ApplicationInfo mLowApplicationInfo;
+    @Mock
+    private PowerWhitelistBackend mPowerWhitelistBackend;
     private BatteryUtils mBatteryUtils;
     private FakeFeatureFactory mFeatureFactory;
     private PowerUsageFeatureProvider mProvider;
@@ -166,9 +171,9 @@
         when(mBatteryStatsHelper.getStats().computeBatteryRealtime(anyLong(), anyInt())).thenReturn(
                 TIME_SINCE_LAST_FULL_CHARGE_US);
 
-        when(mPackageManager.getApplicationInfo(HIGH_SDK_PACKAGE, PackageManager.GET_META_DATA))
+        when(mPackageManager.getApplicationInfo(eq(HIGH_SDK_PACKAGE), anyInt()))
                 .thenReturn(mHighApplicationInfo);
-        when(mPackageManager.getApplicationInfo(LOW_SDK_PACKAGE, PackageManager.GET_META_DATA))
+        when(mPackageManager.getApplicationInfo(eq(LOW_SDK_PACKAGE), anyInt()))
                 .thenReturn(mLowApplicationInfo);
         mHighApplicationInfo.targetSdkVersion = Build.VERSION_CODES.O;
         mLowApplicationInfo.targetSdkVersion = Build.VERSION_CODES.L;
@@ -570,4 +575,42 @@
         assertThat(mBatteryUtils.isAppHeavilyUsed(mBatteryStatsHelper, mUserManager, UID,
                 DISCHARGE_AMOUNT /* threshold */ )).isFalse();
     }
+
+    @Test
+    public void testShouldHideAnomaly_systemAppWithLauncher_returnTrue() {
+        final List<ResolveInfo> resolveInfos = new ArrayList<>();
+        final ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.activityInfo = new ActivityInfo();
+        resolveInfo.activityInfo.packageName = HIGH_SDK_PACKAGE;
+
+        doReturn(resolveInfos).when(mPackageManager).queryIntentActivities(any(), anyInt());
+        doReturn(new String[]{HIGH_SDK_PACKAGE}).when(mPackageManager).getPackagesForUid(UID);
+        mHighApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+
+        assertThat(mBatteryUtils.shouldHideAnomaly(mPowerWhitelistBackend, UID)).isTrue();
+    }
+
+    @Test
+    public void testShouldHideAnomaly_systemAppWithoutLauncher_returnTrue() {
+        doReturn(new ArrayList<>()).when(mPackageManager).queryIntentActivities(any(), anyInt());
+        doReturn(new String[]{HIGH_SDK_PACKAGE}).when(mPackageManager).getPackagesForUid(UID);
+        mHighApplicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+
+        assertThat(mBatteryUtils.shouldHideAnomaly(mPowerWhitelistBackend, UID)).isTrue();
+    }
+
+    @Test
+    public void testShouldHideAnomaly_systemUid_returnTrue() {
+        final int systemUid = Process.ROOT_UID;
+        doReturn(new String[]{HIGH_SDK_PACKAGE}).when(mPackageManager).getPackagesForUid(systemUid);
+
+        assertThat(mBatteryUtils.shouldHideAnomaly(mPowerWhitelistBackend, systemUid)).isTrue();
+    }
+
+    @Test
+    public void testShouldHideAnomaly_normalApp_returnFalse() {
+        doReturn(new String[]{HIGH_SDK_PACKAGE}).when(mPackageManager).getPackagesForUid(UID);
+
+        assertThat(mBatteryUtils.shouldHideAnomaly(mPowerWhitelistBackend, UID)).isFalse();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java
index 49567f6..16d7c3f 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/AnomalyDetectionJobServiceTest.java
@@ -142,6 +142,31 @@
     }
 
     @Test
+    public void testSaveAnomalyToDatabase_systemApp_doNotSaveButLog() {
+        final ArrayList<String> cookies = new ArrayList<>();
+        cookies.add(SUBSCRIBER_COOKIES_AUTO_RESTRICTION);
+        mBundle.putStringArrayList(StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES, cookies);
+        doReturn(SYSTEM_PACKAGE).when(mBatteryUtils).getPackageName(anyInt());
+        doReturn(false).when(mPowerWhitelistBackend).isSysWhitelisted(SYSTEM_PACKAGE);
+        doReturn(Process.FIRST_APPLICATION_UID).when(
+                mAnomalyDetectionJobService).extractUidFromStatsDimensionsValue(any());
+        doReturn(true).when(mBatteryUtils).shouldHideAnomaly(any(), anyInt());
+
+        mAnomalyDetectionJobService.saveAnomalyToDatabase(mContext, mBatteryStatsHelper,
+                mUserManager, mBatteryDatabaseManager, mBatteryUtils, mPolicy,
+                mPowerWhitelistBackend, mContext.getContentResolver(),
+                mFeatureFactory.powerUsageFeatureProvider,
+                mFeatureFactory.metricsFeatureProvider, mBundle);
+
+        verify(mBatteryDatabaseManager, never()).insertAnomaly(anyInt(), anyString(), anyInt(),
+                anyInt(), anyLong());
+        verify(mFeatureFactory.metricsFeatureProvider).action(mContext,
+                MetricsProto.MetricsEvent.ACTION_ANOMALY_IGNORED,
+                SYSTEM_PACKAGE,
+                Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT, ANOMALY_TYPE));
+    }
+
+    @Test
     public void testSaveAnomalyToDatabase_systemUid_doNotSave() {
         doReturn(Process.SYSTEM_UID).when(
                 mAnomalyDetectionJobService).extractUidFromStatsDimensionsValue(any());