Merge "Add location check action for bt anomaly" into oc-dr1-dev am: a6b24388fc

am: 91dbfc6ee9

Change-Id: Ib7f5560b4ef44cb14bcf5e3633b09d179816b221
This commit is contained in:
Lei Yu
2017-06-28 18:13:20 +00:00
committed by android-build-merger
9 changed files with 208 additions and 11 deletions

View File

@@ -4672,11 +4672,11 @@
<!-- Title for location dialog [CHAR LIMIT=60] --> <!-- Title for location dialog [CHAR LIMIT=60] -->
<string name="dialog_location_title">Turn off location?</string> <string name="dialog_location_title">Turn off location?</string>
<!-- Message body for location dialog [CHAR LIMIT=NONE] --> <!-- Message body for location dialog [CHAR LIMIT=NONE] -->
<string name="dialog_location_message" product="default">Your phone cant manage battery normally because <xliff:g id="app">%1$s</xliff:g> keeps requesting your location when you're not using the app.\n\nTo fix this issue, you can turn off location for this app.</string> <string name="dialog_location_message" product="default">Your phone can\'t manage battery normally because <xliff:g id="app">%1$s</xliff:g> keeps requesting your location when you\'re not using the app.\n\nTo fix this issue, you can turn off location for this app.</string>
<!-- Message body for location dialog [CHAR LIMIT=NONE] --> <!-- Message body for location dialog [CHAR LIMIT=NONE] -->
<string name="dialog_location_message" product="tablet">Your tablet cant manage battery normally because <xliff:g id="app">%1$s</xliff:g> keeps requesting your location when you're not using the app.\n\nTo fix this issue, you can turn off location for this app.</string> <string name="dialog_location_message" product="tablet">Your tablet can\'t manage battery normally because <xliff:g id="app">%1$s</xliff:g> keeps requesting your location when you\'re not using the app.\n\nTo fix this issue, you can turn off location for this app.</string>
<!-- Message body for location dialog [CHAR LIMIT=NONE] --> <!-- Message body for location dialog [CHAR LIMIT=NONE] -->
<string name="dialog_location_message" product="device">Your device cant manage battery normally because <xliff:g id="app">%1$s</xliff:g> keeps requesting your location when you're not using the app.\n\nTo fix this issue, you can turn off location for this app.</string> <string name="dialog_location_message" product="device">Your device can\'t manage battery normally because <xliff:g id="app">%1$s</xliff:g> keeps requesting your location when you\'re not using the app.\n\nTo fix this issue, you can turn off location for this app.</string>
<!-- Text for OK button in location dialog [CHAR LIMIT=30] --> <!-- Text for OK button in location dialog [CHAR LIMIT=30] -->
<string name="dialog_location_ok">Turn off</string> <string name="dialog_location_ok">Turn off</string>

View File

@@ -45,10 +45,12 @@ public class Anomaly implements Parcelable {
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({AnomalyActionType.FORCE_STOP, @IntDef({AnomalyActionType.FORCE_STOP,
AnomalyActionType.BACKGROUND_CHECK}) AnomalyActionType.BACKGROUND_CHECK,
AnomalyActionType.LOCATION_CHECK})
public @interface AnomalyActionType { public @interface AnomalyActionType {
int FORCE_STOP = 0; int FORCE_STOP = 0;
int BACKGROUND_CHECK = 1; int BACKGROUND_CHECK = 1;
int LOCATION_CHECK = 2;
} }
@AnomalyType @AnomalyType

View File

@@ -38,7 +38,8 @@ public class AnomalyDialogFragment extends InstrumentedDialogFragment implements
@VisibleForTesting @VisibleForTesting
Anomaly mAnomaly; Anomaly mAnomaly;
private AnomalyUtils mAnomalyUtils; @VisibleForTesting
AnomalyUtils mAnomalyUtils;
/** /**
* Listener to give the control back to target fragment * Listener to give the control back to target fragment
@@ -68,6 +69,11 @@ public class AnomalyDialogFragment extends InstrumentedDialogFragment implements
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
initAnomalyUtils();
}
@VisibleForTesting
void initAnomalyUtils() {
mAnomalyUtils = AnomalyUtils.getInstance(getContext()); mAnomalyUtils = AnomalyUtils.getInstance(getContext());
} }
@@ -114,6 +120,14 @@ public class AnomalyDialogFragment extends InstrumentedDialogFragment implements
.setPositiveButton(R.string.dialog_background_check_ok, this) .setPositiveButton(R.string.dialog_background_check_ok, this)
.setNegativeButton(R.string.dlg_cancel, null) .setNegativeButton(R.string.dlg_cancel, null)
.create(); .create();
case Anomaly.AnomalyActionType.LOCATION_CHECK:
return new AlertDialog.Builder(context)
.setTitle(R.string.dialog_location_title)
.setMessage(getString(R.string.dialog_location_message,
mAnomaly.displayName))
.setPositiveButton(R.string.dialog_location_ok, this)
.setNegativeButton(R.string.dlg_cancel, null)
.create();
default: default:
throw new IllegalArgumentException("unknown type " + mAnomaly.type); throw new IllegalArgumentException("unknown type " + mAnomaly.type);
} }

View File

@@ -22,6 +22,7 @@ import android.support.annotation.VisibleForTesting;
import com.android.settings.fuelgauge.anomaly.action.AnomalyAction; import com.android.settings.fuelgauge.anomaly.action.AnomalyAction;
import com.android.settings.fuelgauge.anomaly.action.BackgroundCheckAction; import com.android.settings.fuelgauge.anomaly.action.BackgroundCheckAction;
import com.android.settings.fuelgauge.anomaly.action.ForceStopAction; import com.android.settings.fuelgauge.anomaly.action.ForceStopAction;
import com.android.settings.fuelgauge.anomaly.action.LocationCheckAction;
import com.android.settings.fuelgauge.anomaly.checker.AnomalyDetector; import com.android.settings.fuelgauge.anomaly.checker.AnomalyDetector;
import com.android.settings.fuelgauge.anomaly.checker.BluetoothScanAnomalyDetector; import com.android.settings.fuelgauge.anomaly.checker.BluetoothScanAnomalyDetector;
import com.android.settings.fuelgauge.anomaly.checker.WakeLockAnomalyDetector; import com.android.settings.fuelgauge.anomaly.checker.WakeLockAnomalyDetector;
@@ -57,8 +58,9 @@ public class AnomalyUtils {
case Anomaly.AnomalyType.WAKE_LOCK: case Anomaly.AnomalyType.WAKE_LOCK:
return new ForceStopAction(mContext); return new ForceStopAction(mContext);
case Anomaly.AnomalyType.WAKEUP_ALARM: case Anomaly.AnomalyType.WAKEUP_ALARM:
case Anomaly.AnomalyType.BLUETOOTH_SCAN:
return new BackgroundCheckAction(mContext); return new BackgroundCheckAction(mContext);
case Anomaly.AnomalyType.BLUETOOTH_SCAN:
return new LocationCheckAction(mContext);
default: default:
return null; return null;
} }

View File

@@ -0,0 +1,59 @@
/*
* 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 android.content.Context;
import android.content.pm.permission.RuntimePermissionPresenter;
import android.support.v4.content.PermissionChecker;
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.overlay.FeatureFactory;
/**
* Location action for anomaly app, which means to turn off location permission for this app
*/
public class LocationCheckAction implements AnomalyAction {
private static final String TAG = "LocationCheckAction";
private static final String LOCATION_PERMISSION = "android.permission-group.LOCATION";
private final Context mContext;
private final RuntimePermissionPresenter mRuntimePermissionPresenter;
public LocationCheckAction(Context context) {
mContext = context;
mRuntimePermissionPresenter = RuntimePermissionPresenter.getInstance(context);
}
@Override
public void handlePositiveAction(Anomaly anomaly, int metricsKey) {
mRuntimePermissionPresenter.revokeRuntimePermission(anomaly.packageName,
LOCATION_PERMISSION);
}
@Override
public boolean isActionActive(Anomaly anomaly) {
return PermissionChecker.checkPermission(mContext, LOCATION_PERMISSION, -1, anomaly.uid,
anomaly.packageName) == PermissionChecker.PERMISSION_GRANTED;
}
@Override
public int getActionType() {
return Anomaly.AnomalyActionType.LOCATION_CHECK;
}
}

View File

@@ -48,15 +48,17 @@ public class BluetoothScanAnomalyDetector implements AnomalyDetector {
private Context mContext; private Context mContext;
public BluetoothScanAnomalyDetector(Context context) { public BluetoothScanAnomalyDetector(Context context) {
this(context, new AnomalyDetectionPolicy(context)); this(context, new AnomalyDetectionPolicy(context),
AnomalyUtils.getInstance(context).getAnomalyAction(
Anomaly.AnomalyType.BLUETOOTH_SCAN));
} }
@VisibleForTesting @VisibleForTesting
BluetoothScanAnomalyDetector(Context context, AnomalyDetectionPolicy policy) { BluetoothScanAnomalyDetector(Context context, AnomalyDetectionPolicy policy,
AnomalyAction anomalyAction) {
mContext = context; mContext = context;
mBatteryUtils = BatteryUtils.getInstance(context); mBatteryUtils = BatteryUtils.getInstance(context);
mAnomalyAction = AnomalyUtils.getInstance(context).getAnomalyAction( mAnomalyAction = anomalyAction;
Anomaly.AnomalyType.BLUETOOTH_SCAN);
mBluetoothScanningThreshold = policy.bluetoothScanThreshold; mBluetoothScanningThreshold = policy.bluetoothScanThreshold;
} }

View File

@@ -18,6 +18,10 @@ package com.android.settings.fuelgauge.anomaly;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.robolectric.Shadows.shadowOf; import static org.robolectric.Shadows.shadowOf;
import android.app.AlertDialog; import android.app.AlertDialog;
@@ -25,12 +29,15 @@ import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.fuelgauge.anomaly.action.AnomalyAction;
import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig; import com.android.settings.TestConfig;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowAlertDialog; import org.robolectric.shadows.ShadowAlertDialog;
@@ -43,13 +50,21 @@ public class AnomalyDialogFragmentTest {
private static final String PACKAGE_NAME = "com.android.app"; private static final String PACKAGE_NAME = "com.android.app";
private static final String DISPLAY_NAME = "app"; private static final String DISPLAY_NAME = "app";
private static final int UID = 111; private static final int UID = 111;
@Mock
private AnomalyUtils mAnomalyUtils;
@Mock
private AnomalyAction mAnomalyAction;
private Anomaly mWakeLockAnomaly; private Anomaly mWakeLockAnomaly;
private Anomaly mWakeupAlarmAnomaly; private Anomaly mWakeupAlarmAnomaly;
private Anomaly mBluetoothAnomaly;
private AnomalyDialogFragment mAnomalyDialogFragment; private AnomalyDialogFragment mAnomalyDialogFragment;
private Context mContext; private Context mContext;
@Before @Before
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application; mContext = RuntimeEnvironment.application;
mWakeLockAnomaly = new Anomaly.Builder() mWakeLockAnomaly = new Anomaly.Builder()
.setType(Anomaly.AnomalyType.WAKE_LOCK) .setType(Anomaly.AnomalyType.WAKE_LOCK)
@@ -63,6 +78,12 @@ public class AnomalyDialogFragmentTest {
.setPackageName(PACKAGE_NAME) .setPackageName(PACKAGE_NAME)
.setDisplayName(DISPLAY_NAME) .setDisplayName(DISPLAY_NAME)
.build(); .build();
mBluetoothAnomaly = new Anomaly.Builder()
.setType(Anomaly.AnomalyType.BLUETOOTH_SCAN)
.setUid(UID)
.setPackageName(PACKAGE_NAME)
.setDisplayName(DISPLAY_NAME)
.build();
} }
@Test @Test
@@ -114,4 +135,29 @@ public class AnomalyDialogFragmentTest {
assertThat(dialog.getButton(DialogInterface.BUTTON_NEGATIVE).getText()).isEqualTo( assertThat(dialog.getButton(DialogInterface.BUTTON_NEGATIVE).getText()).isEqualTo(
mContext.getString(R.string.dlg_cancel)); mContext.getString(R.string.dlg_cancel));
} }
@Test
public void testOnCreateDialog_bluetoothAnomaly_fireLocationCheckDialog() {
mAnomalyDialogFragment = spy(AnomalyDialogFragment.newInstance(mBluetoothAnomaly,
0 /* metricskey */));
mAnomalyDialogFragment.mAnomalyUtils = mAnomalyUtils;
doReturn(mAnomalyAction).when(mAnomalyUtils).getAnomalyAction(anyInt());
doNothing().when(mAnomalyDialogFragment).initAnomalyUtils();
doReturn(Anomaly.AnomalyActionType.LOCATION_CHECK).when(mAnomalyAction).getActionType();
FragmentTestUtil.startFragment(mAnomalyDialogFragment);
final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
ShadowAlertDialog shadowDialog = shadowOf(dialog);
assertThat(shadowDialog.getMessage()).isEqualTo(
mContext.getString(R.string.dialog_location_message,
mWakeLockAnomaly.displayName));
assertThat(shadowDialog.getTitle()).isEqualTo(
mContext.getString(R.string.dialog_location_title));
assertThat(dialog.getButton(DialogInterface.BUTTON_POSITIVE).getText()).isEqualTo(
mContext.getString(R.string.dialog_location_ok));
assertThat(dialog.getButton(DialogInterface.BUTTON_NEGATIVE).getText()).isEqualTo(
mContext.getString(R.string.dlg_cancel));
}
} }

View File

@@ -106,7 +106,8 @@ public class BluetoothScanAnomalyDetectorTest {
mUsageList.add(mTargetSipper); mUsageList.add(mTargetSipper);
doReturn(mUsageList).when(mBatteryStatsHelper).getUsageList(); doReturn(mUsageList).when(mBatteryStatsHelper).getUsageList();
mBluetoothScanAnomalyDetector = spy(new BluetoothScanAnomalyDetector(mContext, mPolicy)); mBluetoothScanAnomalyDetector = spy(
new BluetoothScanAnomalyDetector(mContext, mPolicy, mAnomalyAction));
mBluetoothScanAnomalyDetector.mBatteryUtils = mBatteryUtils; mBluetoothScanAnomalyDetector.mBatteryUtils = mBatteryUtils;
mBluetoothScanAnomalyDetector.mAnomalyAction = mAnomalyAction; mBluetoothScanAnomalyDetector.mAnomalyAction = mAnomalyAction;
doReturn(false).when(mBatteryUtils).shouldHideSipper(any()); doReturn(false).when(mBatteryUtils).shouldHideSipper(any());

View File

@@ -0,0 +1,71 @@
/*
* 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 com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.content.pm.PackageManager;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class LocationCheckActionTest {
private static final String PACKAGE_NAME = "com.android.chrome";
private Context mContext;
private LocationCheckAction mLocationCheckAction;
private Anomaly mAnomaly;
@Before
public void setUp() {
mContext = InstrumentationRegistry.getTargetContext();
mLocationCheckAction = new LocationCheckAction(mContext);
mAnomaly = new Anomaly.Builder()
.setUid(getPackageUid(mContext, PACKAGE_NAME))
.setPackageName(PACKAGE_NAME)
.build();
}
@Test
public void testRevokeAndCheck() {
mLocationCheckAction.handlePositiveAction(mAnomaly, 0 /* metric key */);
assertThat(mLocationCheckAction.isActionActive(mAnomaly)).isFalse();
}
private int getPackageUid(Context context, String packageName) {
try {
return context.getPackageManager().getPackageUid(packageName,
PackageManager.GET_META_DATA);
} catch (PackageManager.NameNotFoundException e) {
return -1;
}
}
}