Merge "Added unit tests for MasterClearReceiver."
This commit is contained in:
committed by
Android (Google) Code Review
commit
d93b5e0e0a
@@ -25,7 +25,6 @@ import android.os.RecoverySystem;
|
||||
import android.os.UserHandle;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
import android.view.WindowManager;
|
||||
|
||||
@@ -59,6 +58,7 @@ public class MasterClearReceiver extends BroadcastReceiver {
|
||||
.getString(com.android.internal.R.string.config_factoryResetPackage);
|
||||
if (Intent.ACTION_FACTORY_RESET.equals(intent.getAction())
|
||||
&& !TextUtils.isEmpty(factoryResetPackage)) {
|
||||
Slog.i(TAG, "Re-directing intent to " + factoryResetPackage);
|
||||
intent.setPackage(factoryResetPackage).setComponent(null);
|
||||
context.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
|
||||
return;
|
||||
@@ -77,9 +77,12 @@ public class MasterClearReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Slog.i(TAG, "Calling RecoverySystem.rebootWipeUserData(context, "
|
||||
+ "shutdown=" + shutdown + ", reason=" + reason
|
||||
+ ", forceWipe=" + forceWipe + ", wipeEsims=" + mWipeEsims + ")");
|
||||
RecoverySystem
|
||||
.rebootWipeUserData(context, shutdown, reason, forceWipe, mWipeEsims);
|
||||
Log.wtf(TAG, "Still running after master clear?!");
|
||||
Slog.wtf(TAG, "Still running after master clear?!");
|
||||
} catch (IOException e) {
|
||||
Slog.e(TAG, "Can't perform master clear/factory reset", e);
|
||||
} catch (SecurityException e) {
|
||||
@@ -90,8 +93,10 @@ public class MasterClearReceiver extends BroadcastReceiver {
|
||||
|
||||
if (mWipeExternalStorage) {
|
||||
// thr will be started at the end of this task.
|
||||
Slog.i(TAG, "Wiping external storage on async task");
|
||||
new WipeDataTask(context, thr).execute();
|
||||
} else {
|
||||
Slog.i(TAG, "NOT wiping external storage; starting thread " + thr.getName());
|
||||
thr.start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
<uses-permission android:name="android.permission.MANAGE_APPOPS"/>
|
||||
<uses-permission android:name="android.permission.MONITOR_DEVICE_CONFIG_ACCESS"/>
|
||||
|
||||
<!-- needed by MasterClearReceiverTest to display a system dialog -->
|
||||
<uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
|
||||
|
||||
<application android:testOnly="true"
|
||||
android:debuggable="true">
|
||||
<uses-library android:name="android.test.runner" />
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.server;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
|
||||
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Mockito.never;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.Intent;
|
||||
import android.os.Looper;
|
||||
import android.os.RecoverySystem;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.util.Log;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoSession;
|
||||
import org.mockito.quality.Strictness;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Run it as {@code FrameworksMockingServicesTests:MasterClearReceiverTest}.
|
||||
*/
|
||||
@Presubmit
|
||||
public final class MasterClearReceiverTest {
|
||||
|
||||
private static final String TAG = MasterClearReceiverTest.class.getSimpleName();
|
||||
|
||||
private MockitoSession mSession;
|
||||
|
||||
// Cannot @Mock context because MasterClearReceiver shows an AlertDialog, which relies
|
||||
// on resources - we'd need to mock them as well.
|
||||
private final Context mContext = new ContextWrapper(
|
||||
InstrumentationRegistry.getInstrumentation().getTargetContext()) {
|
||||
|
||||
@Override
|
||||
public Object getSystemService(String name) {
|
||||
Log.v(TAG, "getSystemService(): " + name);
|
||||
return name.equals(Context.STORAGE_SERVICE) ? mSm : super.getSystemService(name);
|
||||
}
|
||||
};
|
||||
|
||||
private final MasterClearReceiver mReceiver = new MasterClearReceiver();
|
||||
|
||||
// Used to make sure that wipeAdoptableDisks() is called before rebootWipeUserData()
|
||||
private boolean mWipeExternalDataCalled;
|
||||
|
||||
// Uset to block test until rebootWipeUserData() is called, as it might be asynchronous called
|
||||
// in a different thread
|
||||
private final CountDownLatch mRebootWipeUserDataLatch = new CountDownLatch(1);
|
||||
|
||||
@Mock
|
||||
private StorageManager mSm;
|
||||
|
||||
@Mock
|
||||
private WindowManager mWm;
|
||||
|
||||
@Before
|
||||
public void startSession() {
|
||||
mSession = mockitoSession()
|
||||
.initMocks(this)
|
||||
.mockStatic(RecoverySystem.class)
|
||||
.strictness(Strictness.LENIENT)
|
||||
.startMocking();
|
||||
}
|
||||
|
||||
@After
|
||||
public void finishSession() {
|
||||
if (mSession == null) {
|
||||
Log.w(TAG, "finishSession(): no session");
|
||||
return;
|
||||
}
|
||||
mSession.finishMocking();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoExtras() throws Exception {
|
||||
expectNoWipeExternalData();
|
||||
expectRebootWipeUserData();
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_FACTORY_RESET);
|
||||
mReceiver.onReceive(mContext, intent);
|
||||
|
||||
verifyRebootWipeUserData();
|
||||
verifyNoWipeExternalData();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWipeExternalDirectory() throws Exception {
|
||||
expectWipeExternalData();
|
||||
expectRebootWipeUserData();
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_FACTORY_RESET);
|
||||
intent.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, true);
|
||||
mReceiver.onReceive(mContext, intent);
|
||||
|
||||
verifyRebootWipeUserData();
|
||||
verifyWipeExternalData();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllExtras() throws Exception {
|
||||
expectWipeExternalData();
|
||||
expectRebootWipeUserData();
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_FACTORY_RESET);
|
||||
intent.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, true);
|
||||
intent.putExtra("shutdown", true);
|
||||
intent.putExtra(Intent.EXTRA_REASON, "Self destruct");
|
||||
intent.putExtra(Intent.EXTRA_FORCE_FACTORY_RESET, true);
|
||||
intent.putExtra(Intent.EXTRA_WIPE_ESIMS, true);
|
||||
mReceiver.onReceive(mContext, intent);
|
||||
|
||||
verifyRebootWipeUserData(/* shutdown= */ true, /* reason= */ "Self destruct",
|
||||
/* force= */ true, /* wipeEuicc= */ true);
|
||||
verifyWipeExternalData();
|
||||
}
|
||||
|
||||
|
||||
private void expectNoWipeExternalData() {
|
||||
// This is a trick to simplify how the order of methods are called: as wipeAdoptableDisks()
|
||||
// should be called before rebootWipeUserData(), expectRebootWipeUserData() throws an
|
||||
// exception if it's not called, so this method "emulates" a call when it's not neeeded.
|
||||
//
|
||||
// A more robust solution would be using internal counters for expected and actual mocked
|
||||
// calls, so the expectXXX() methods would increment expected counter and the Answer
|
||||
// implementations would increment the actual counter and check if they match, but that
|
||||
// would be an overkill (and make the test logic more complicated).
|
||||
mWipeExternalDataCalled = true;
|
||||
}
|
||||
|
||||
private void expectRebootWipeUserData() {
|
||||
doAnswer((inv) -> {
|
||||
Log.i(TAG, inv.toString());
|
||||
if (!mWipeExternalDataCalled) {
|
||||
String error = "rebootWipeUserData() called before wipeAdoptableDisks()";
|
||||
Log.e(TAG, error);
|
||||
throw new IllegalStateException(error);
|
||||
}
|
||||
mRebootWipeUserDataLatch.countDown();
|
||||
return null;
|
||||
}).when(() -> RecoverySystem
|
||||
.rebootWipeUserData(any(), anyBoolean(), any(), anyBoolean(), anyBoolean()));
|
||||
}
|
||||
|
||||
private void expectWipeExternalData() {
|
||||
Looper.prepare(); // needed by Dialog
|
||||
|
||||
doAnswer((inv) -> {
|
||||
Log.i(TAG, inv.toString());
|
||||
mWipeExternalDataCalled = true;
|
||||
return null;
|
||||
}).when(mSm).wipeAdoptableDisks();
|
||||
}
|
||||
|
||||
private void verifyRebootWipeUserData() throws Exception {
|
||||
verifyRebootWipeUserData(/* shutdown= */ false, /* reason= */ null, /* force= */ false,
|
||||
/* wipeEuicc= */ false);
|
||||
|
||||
}
|
||||
|
||||
private void verifyRebootWipeUserData(boolean shutdown, String reason, boolean force,
|
||||
boolean wipeEuicc) throws Exception {
|
||||
boolean called = mRebootWipeUserDataLatch.await(5, TimeUnit.SECONDS);
|
||||
assertWithMessage("rebootWipeUserData not called in 5s").that(called).isTrue();
|
||||
|
||||
verify(()-> RecoverySystem.rebootWipeUserData(same(mContext), eq(shutdown), eq(reason),
|
||||
eq(force), eq(wipeEuicc)));
|
||||
}
|
||||
|
||||
private void verifyWipeExternalData() {
|
||||
verify(mSm).wipeAdoptableDisks();
|
||||
}
|
||||
|
||||
private void verifyNoWipeExternalData() {
|
||||
verify(mSm, never()).wipeAdoptableDisks();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user