diff --git a/src/com/android/settings/fuelgauge/PowerUsageAnomalyDetails.java b/src/com/android/settings/fuelgauge/PowerUsageAnomalyDetails.java index 9a6aed22a52..085b88fe995 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageAnomalyDetails.java +++ b/src/com/android/settings/fuelgauge/PowerUsageAnomalyDetails.java @@ -28,6 +28,7 @@ import android.support.v7.preference.PreferenceGroup; import android.util.IconDrawableFactory; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.core.PreferenceController; @@ -92,7 +93,8 @@ public class PowerUsageAnomalyDetails extends DashboardFragment implements AnomalyPreference anomalyPreference = (AnomalyPreference) preference; final Anomaly anomaly = anomalyPreference.getAnomaly(); - AnomalyDialogFragment dialogFragment = AnomalyDialogFragment.newInstance(anomaly); + AnomalyDialogFragment dialogFragment = AnomalyDialogFragment.newInstance(anomaly, + MetricsProto.MetricsEvent.FUELGAUGE_ANOMALY_DETAIL); dialogFragment.setTargetFragment(this, REQUEST_ANOMALY_ACTION); dialogFragment.show(getFragmentManager(), TAG); diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java index 0ac0c6008a2..7f25dc3f1d4 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java +++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java @@ -169,7 +169,7 @@ public class PowerUsageSummary extends PowerUsageBase implements KEY_TIME_SINCE_LAST_FULL_CHARGE); mFooterPreferenceMixin.createFooterPreference().setTitle(R.string.battery_footer_summary); mAnomalySummaryPreferenceController = new AnomalySummaryPreferenceController( - (SettingsActivity) getActivity(), this); + (SettingsActivity) getActivity(), this, MetricsEvent.FUELGAUGE_POWER_USAGE_SUMMARY); mBatteryUtils = BatteryUtils.getInstance(getContext()); mAnomalySparseArray = new SparseArray<>(); diff --git a/src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragment.java b/src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragment.java index 122b478e8d4..e05eace63d4 100644 --- a/src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragment.java +++ b/src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragment.java @@ -34,9 +34,11 @@ public class AnomalyDialogFragment extends InstrumentedDialogFragment implements DialogInterface.OnClickListener { private static final String ARG_ANOMALY = "anomaly"; + private static final String ARG_METRICS_KEY = "metrics_key"; @VisibleForTesting Anomaly mAnomaly; + private AnomalyUtils mAnomalyUtils; /** * Listener to give the control back to target fragment @@ -52,16 +54,23 @@ public class AnomalyDialogFragment extends InstrumentedDialogFragment implements void onAnomalyHandled(Anomaly anomaly); } - public static AnomalyDialogFragment newInstance(Anomaly anomaly) { + public static AnomalyDialogFragment newInstance(Anomaly anomaly, int metricsKey) { AnomalyDialogFragment dialogFragment = new AnomalyDialogFragment(); - Bundle args = new Bundle(1); + Bundle args = new Bundle(2); args.putParcelable(ARG_ANOMALY, anomaly); + args.putInt(ARG_METRICS_KEY, metricsKey); dialogFragment.setArguments(args); return dialogFragment; } + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mAnomalyUtils = AnomalyUtils.getInstance(getContext()); + } + @Override public int getMetricsCategory() { // TODO(b/37681923): add anomaly metric id @@ -75,8 +84,10 @@ public class AnomalyDialogFragment extends InstrumentedDialogFragment implements return; } - final AnomalyAction anomalyAction = AnomalyUtils.getAnomalyAction(mAnomaly.type); - anomalyAction.handlePositiveAction(mAnomaly.packageName); + final AnomalyAction anomalyAction = mAnomalyUtils.getAnomalyAction(mAnomaly.type); + final int metricsKey = getArguments().getInt(ARG_METRICS_KEY); + + anomalyAction.handlePositiveAction(mAnomaly.packageName, metricsKey); lsn.onAnomalyHandled(mAnomaly); } @@ -86,7 +97,7 @@ public class AnomalyDialogFragment extends InstrumentedDialogFragment implements mAnomaly = bundle.getParcelable(ARG_ANOMALY); final Context context = getContext(); - final AnomalyAction anomalyAction = AnomalyUtils.getAnomalyAction(mAnomaly.type); + final AnomalyAction anomalyAction = mAnomalyUtils.getAnomalyAction(mAnomaly.type); switch (anomalyAction.getActionType()) { case Anomaly.AnomalyActionType.FORCE_STOP: return new AlertDialog.Builder(context) diff --git a/src/com/android/settings/fuelgauge/anomaly/AnomalySummaryPreferenceController.java b/src/com/android/settings/fuelgauge/anomaly/AnomalySummaryPreferenceController.java index 49fe4e8b49c..23b4e77051f 100644 --- a/src/com/android/settings/fuelgauge/anomaly/AnomalySummaryPreferenceController.java +++ b/src/com/android/settings/fuelgauge/anomaly/AnomalySummaryPreferenceController.java @@ -42,11 +42,19 @@ public class AnomalySummaryPreferenceController { List mAnomalies; private SettingsActivity mSettingsActivity; + /** + * Metrics key about fragment that create this controller + * + * @see com.android.internal.logging.nano.MetricsProto.MetricsEvent + */ + private int mMetricsKey; + public AnomalySummaryPreferenceController(SettingsActivity activity, - PreferenceFragment fragment) { + PreferenceFragment fragment, int metricsKey) { mFragment = fragment; mSettingsActivity = activity; mAnomalyPreference = mFragment.getPreferenceScreen().findPreference(ANOMALY_KEY); + mMetricsKey = metricsKey; hideHighUsagePreference(); } @@ -54,7 +62,8 @@ public class AnomalySummaryPreferenceController { if (mAnomalies != null && ANOMALY_KEY.equals(preference.getKey())) { if (mAnomalies.size() == 1) { final Anomaly anomaly = mAnomalies.get(0); - AnomalyDialogFragment dialogFragment = AnomalyDialogFragment.newInstance(anomaly); + AnomalyDialogFragment dialogFragment = AnomalyDialogFragment.newInstance(anomaly, + mMetricsKey); dialogFragment.setTargetFragment(mFragment, REQUEST_ANOMALY_ACTION); dialogFragment.show(mFragment.getFragmentManager(), TAG); } else { diff --git a/src/com/android/settings/fuelgauge/anomaly/AnomalyUtils.java b/src/com/android/settings/fuelgauge/anomaly/AnomalyUtils.java index 987482e1076..a6efab50242 100644 --- a/src/com/android/settings/fuelgauge/anomaly/AnomalyUtils.java +++ b/src/com/android/settings/fuelgauge/anomaly/AnomalyUtils.java @@ -16,23 +16,41 @@ package com.android.settings.fuelgauge.anomaly; +import android.content.Context; +import android.support.annotation.VisibleForTesting; + import com.android.settings.fuelgauge.anomaly.action.AnomalyAction; import com.android.settings.fuelgauge.anomaly.action.ForceStopAction; /** - * Utitily class for anomaly detection + * Utility class for anomaly detection */ public class AnomalyUtils { + private Context mContext; + private static AnomalyUtils sInstance; + + @VisibleForTesting + AnomalyUtils(Context context) { + mContext = context.getApplicationContext(); + } + + public static AnomalyUtils getInstance(Context context) { + if (sInstance == null) { + sInstance = new AnomalyUtils(context); + } + return sInstance; + } /** - * Return the corresponding {@link AnomalyAction} according to {@link AnomalyType} + * Return the corresponding {@link AnomalyAction} according to + * {@link com.android.settings.fuelgauge.anomaly.Anomaly.AnomalyType} * * @return corresponding {@link AnomalyAction}, or null if cannot find it. */ - public static final AnomalyAction getAnomalyAction(int anomalyType) { + public final AnomalyAction getAnomalyAction(@Anomaly.AnomalyType int anomalyType) { switch (anomalyType) { case Anomaly.AnomalyType.WAKE_LOCK: - return new ForceStopAction(); + return new ForceStopAction(mContext); default: return null; } diff --git a/src/com/android/settings/fuelgauge/anomaly/action/AnomalyAction.java b/src/com/android/settings/fuelgauge/anomaly/action/AnomalyAction.java index 80137973d58..6b8ff678ace 100644 --- a/src/com/android/settings/fuelgauge/anomaly/action/AnomalyAction.java +++ b/src/com/android/settings/fuelgauge/anomaly/action/AnomalyAction.java @@ -20,6 +20,13 @@ package com.android.settings.fuelgauge.anomaly.action; * Interface for anomaly action, which is triggered if we need to handle the anomaly */ public interface AnomalyAction { - void handlePositiveAction(String packageName); + /** + * handle the action when user clicks positive button + * @param packageName about the app that we need to handle + * @param metricsKey key for the page that invokes the action + * + * @see com.android.internal.logging.nano.MetricsProto + */ + void handlePositiveAction(String packageName, int metricsKey); int getActionType(); } diff --git a/src/com/android/settings/fuelgauge/anomaly/action/ForceStopAction.java b/src/com/android/settings/fuelgauge/anomaly/action/ForceStopAction.java index be5d6ab1958..40f6839eaa7 100644 --- a/src/com/android/settings/fuelgauge/anomaly/action/ForceStopAction.java +++ b/src/com/android/settings/fuelgauge/anomaly/action/ForceStopAction.java @@ -16,15 +16,39 @@ package com.android.settings.fuelgauge.anomaly.action; +import android.app.ActivityManager; +import android.content.Context; +import android.util.Pair; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.fuelgauge.anomaly.Anomaly; +import com.android.settings.overlay.FeatureFactory; /** * Force stop action for anomaly app, which means to stop the app which causes anomaly */ public class ForceStopAction implements AnomalyAction { + + private Context mContext; + private MetricsFeatureProvider mMetricsFeatureProvider; + private ActivityManager mActivityManager; + + public ForceStopAction(Context context) { + mContext = context; + mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); + mActivityManager = (ActivityManager) context.getSystemService( + Context.ACTIVITY_SERVICE); + } + @Override - public void handlePositiveAction(String packageName) { + public void handlePositiveAction(String packageName, int metricsKey) { // force stop the package + mMetricsFeatureProvider.action(mContext, + MetricsProto.MetricsEvent.ACTION_APP_FORCE_STOP, packageName, + Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT, metricsKey)); + + mActivityManager.forceStopPackage(packageName); } @Override diff --git a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragmentTest.java b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragmentTest.java index 95a8ff33cdc..2fb8ba5d4ce 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragmentTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyDialogFragmentTest.java @@ -57,7 +57,7 @@ public class AnomalyDialogFragmentTest { .setPackageName(PACKAGE_NAME) .build(); - mAnomalyDialogFragment = AnomalyDialogFragment.newInstance(mAnomaly); + mAnomalyDialogFragment = AnomalyDialogFragment.newInstance(mAnomaly, 0 /* metricskey */); } @Test diff --git a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalySummaryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalySummaryPreferenceControllerTest.java index 6a68fd23514..3f338ea598a 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalySummaryPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalySummaryPreferenceControllerTest.java @@ -84,7 +84,7 @@ public class AnomalySummaryPreferenceControllerTest { mAnomalyList = new ArrayList<>(); mAnomalySummaryPreferenceController = new AnomalySummaryPreferenceController( - mSettingsActivity, mFragment); + mSettingsActivity, mFragment, 0 /* metricskey */); } @Test diff --git a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyUtilsTest.java index 8d6d31baece..ae211ce88d8 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyUtilsTest.java @@ -22,17 +22,25 @@ import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; import com.android.settings.fuelgauge.anomaly.action.ForceStopAction; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; @RunWith(SettingsRobolectricTestRunner.class) @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) public class AnomalyUtilsTest { + private AnomalyUtils mAnomalyUtils; + + @Before + public void setUp() { + mAnomalyUtils = new AnomalyUtils(RuntimeEnvironment.application); + } @Test public void testGetAnomalyAction_typeWakeLock_returnForceStop() { - assertThat(AnomalyUtils.getAnomalyAction(Anomaly.AnomalyType.WAKE_LOCK)).isInstanceOf( + assertThat(mAnomalyUtils.getAnomalyAction(Anomaly.AnomalyType.WAKE_LOCK)).isInstanceOf( ForceStopAction.class); } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/action/ForceStopActionTest.java b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/action/ForceStopActionTest.java new file mode 100644 index 00000000000..e4bcb1d4379 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/action/ForceStopActionTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.fuelgauge.anomaly.action; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; + +import android.app.ActivityManager; +import android.content.Context; + +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import com.android.settings.testutils.FakeFeatureFactory; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class ForceStopActionTest { + private static final String PACKAGE_NAME = "com.android.app"; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Context mContext; + @Mock + private ActivityManager mActivityManager; + private ForceStopAction mForceStopAction; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + FakeFeatureFactory.setupForTest(mContext); + doReturn(mActivityManager).when(mContext).getSystemService(Context.ACTIVITY_SERVICE); + + mForceStopAction = new ForceStopAction(mContext); + } + + @Test + public void testHandlePositiveAction_forceStopPackage() { + mForceStopAction.handlePositiveAction(PACKAGE_NAME, 0 /* metricskey */); + + verify(mActivityManager).forceStopPackage(PACKAGE_NAME); + } +}