• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server;
18 
19 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
20 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
21 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
22 
23 import static com.google.common.truth.Truth.assertWithMessage;
24 
25 import static org.mockito.ArgumentMatchers.any;
26 import static org.mockito.ArgumentMatchers.anyBoolean;
27 import static org.mockito.ArgumentMatchers.anyString;
28 import static org.mockito.ArgumentMatchers.eq;
29 import static org.mockito.ArgumentMatchers.same;
30 import static org.mockito.Mockito.never;
31 import static org.mockito.Mockito.when;
32 
33 import android.app.Activity;
34 import android.content.BroadcastReceiver;
35 import android.content.Context;
36 import android.content.ContextWrapper;
37 import android.content.Intent;
38 import android.os.Looper;
39 import android.os.RecoverySystem;
40 import android.os.UserHandle;
41 import android.os.UserManager;
42 import android.os.storage.StorageManager;
43 import android.platform.test.annotations.Presubmit;
44 import android.util.Log;
45 
46 import androidx.test.InstrumentationRegistry;
47 
48 import org.junit.After;
49 import org.junit.Before;
50 import org.junit.Test;
51 import org.mockito.Mock;
52 import org.mockito.MockitoSession;
53 import org.mockito.quality.Strictness;
54 
55 import java.util.concurrent.CountDownLatch;
56 import java.util.concurrent.TimeUnit;
57 
58 /**
59  * Run it as {@code FrameworksMockingServicesTests:MasterClearReceiverTest}.
60  */
61 @Presubmit
62 public final class MasterClearReceiverTest {
63 
64     private static final String TAG = MasterClearReceiverTest.class.getSimpleName();
65 
66     private MockitoSession mSession;
67 
68     // Cannot @Mock context because MasterClearReceiver shows an AlertDialog, which relies
69     // on resources - we'd need to mock them as well.
70     private final Context mContext = new ContextWrapper(
71             InstrumentationRegistry.getInstrumentation().getTargetContext()) {
72 
73         @Override
74         public Object getSystemService(String name) {
75             Log.v(TAG, "getSystemService(): " + name);
76             if (name.equals(Context.STORAGE_SERVICE)) {
77                 return mSm;
78             }
79             if (name.equals(Context.USER_SERVICE)) {
80                 return mUserManager;
81             }
82             return super.getSystemService(name);
83         }
84     };
85 
86     private final MasterClearReceiver mReceiver = new MasterClearReceiver();
87 
88     // Used to make sure that wipeAdoptableDisks() is called before rebootWipeUserData()
89     private boolean mWipeExternalDataCalled;
90 
91     // Uset to block test until rebootWipeUserData() is called, as it might be asynchronous called
92     // in a different thread
93     private final CountDownLatch mRebootWipeUserDataLatch = new CountDownLatch(1);
94 
95     @Mock
96     private StorageManager mSm;
97 
98     @Mock
99     private UserManager mUserManager;
100 
101     @Before
startSession()102     public void startSession() {
103         mSession = mockitoSession()
104                 .initMocks(this)
105                 .mockStatic(RecoverySystem.class)
106                 .mockStatic(UserManager.class)
107                 .strictness(Strictness.LENIENT)
108                 .startMocking();
109         setPendingResultForUser(UserHandle.myUserId());
110     }
111 
112     @After
finishSession()113     public void finishSession() {
114         if (mSession == null) {
115             Log.w(TAG, "finishSession(): no session");
116             return;
117         }
118         mSession.finishMocking();
119     }
120 
121     @Test
testNoExtras()122     public void testNoExtras() throws Exception {
123         expectNoWipeExternalData();
124         expectRebootWipeUserData();
125 
126         Intent intent = new Intent(Intent.ACTION_FACTORY_RESET);
127         mReceiver.onReceive(mContext, intent);
128 
129         verifyRebootWipeUserData();
130         verifyNoWipeExternalData();
131     }
132 
133     @Test
testWipeExternalDirectory()134     public void testWipeExternalDirectory() throws Exception {
135         expectWipeExternalData();
136         expectRebootWipeUserData();
137 
138         Intent intent = new Intent(Intent.ACTION_FACTORY_RESET);
139         intent.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, true);
140         mReceiver.onReceive(mContext, intent);
141 
142         verifyRebootWipeUserData();
143         verifyWipeExternalData();
144     }
145 
146     @Test
testAllExtras()147     public void testAllExtras() throws Exception {
148         expectWipeExternalData();
149         expectRebootWipeUserData();
150 
151         Intent intent = new Intent(Intent.ACTION_FACTORY_RESET);
152         intent.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, true);
153         intent.putExtra("shutdown", true);
154         intent.putExtra(Intent.EXTRA_REASON, "Self destruct");
155         intent.putExtra(Intent.EXTRA_FORCE_FACTORY_RESET, true);
156         intent.putExtra(Intent.EXTRA_WIPE_ESIMS, true);
157         mReceiver.onReceive(mContext, intent);
158 
159         verifyRebootWipeUserData(/* shutdown= */ true, /* reason= */ "Self destruct",
160                 /* force= */ true, /* wipeEuicc= */ true);
161         verifyWipeExternalData();
162     }
163 
164     @Test
testNonSystemUser()165     public void testNonSystemUser() throws Exception {
166         expectWipeNonSystemUser();
167 
168         Intent intent = new Intent(Intent.ACTION_FACTORY_RESET);
169         setPendingResultForUser(/* userId= */ 10);
170         mReceiver.onReceive(mContext, intent);
171 
172         verifyNoRebootWipeUserData();
173         verifyNoWipeExternalData();
174         verifyWipeNonSystemUser();
175     }
176 
177     @Test
testHeadlessSystemUser()178     public void testHeadlessSystemUser() throws Exception {
179         expectNoWipeExternalData();
180         expectRebootWipeUserData();
181         expectHeadlessSystemUserMode();
182 
183         Intent intent = new Intent(Intent.ACTION_FACTORY_RESET);
184         setPendingResultForUser(/* userId= */ 10);
185         mReceiver.onReceive(mContext, intent);
186 
187         verifyRebootWipeUserData();
188         verifyNoWipeExternalData();
189     }
190 
expectNoWipeExternalData()191     private void expectNoWipeExternalData() {
192         // This is a trick to simplify how the order of methods are called: as wipeAdoptableDisks()
193         // should be called before rebootWipeUserData(), expectRebootWipeUserData() throws an
194         // exception if it's not called, so this method "emulates" a call when it's not neeeded.
195         //
196         // A more robust solution would be using internal counters for expected and actual mocked
197         // calls, so the expectXXX() methods would increment expected counter and the Answer
198         // implementations would increment the actual counter and check if they match, but that
199         // would be an overkill (and make the test logic more complicated).
200         mWipeExternalDataCalled = true;
201     }
202 
expectRebootWipeUserData()203     private void expectRebootWipeUserData() {
204         doAnswer((inv) -> {
205             Log.i(TAG, inv.toString());
206             if (!mWipeExternalDataCalled) {
207                 String error = "rebootWipeUserData() called before wipeAdoptableDisks()";
208                 Log.e(TAG, error);
209                 throw new IllegalStateException(error);
210             }
211             mRebootWipeUserDataLatch.countDown();
212             return null;
213         }).when(() -> RecoverySystem
214                 .rebootWipeUserData(any(), anyBoolean(), any(), anyBoolean(), anyBoolean()));
215     }
216 
expectWipeExternalData()217     private void expectWipeExternalData() {
218         Looper.prepare(); // needed by Dialog
219 
220         doAnswer((inv) -> {
221             Log.i(TAG, inv.toString());
222             mWipeExternalDataCalled = true;
223             return null;
224         }).when(mSm).wipeAdoptableDisks();
225     }
226 
expectWipeNonSystemUser()227     private void expectWipeNonSystemUser() {
228         when(mUserManager.removeUserWhenPossible(any(), anyBoolean()))
229                 .thenReturn(UserManager.REMOVE_RESULT_REMOVED);
230     }
231 
expectHeadlessSystemUserMode()232     private void expectHeadlessSystemUserMode() {
233         doAnswer((inv) -> {
234             Log.i(TAG, inv.toString());
235             return true;
236         }).when(() -> UserManager.isHeadlessSystemUserMode());
237     }
238 
verifyRebootWipeUserData()239     private void verifyRebootWipeUserData() throws Exception  {
240         verifyRebootWipeUserData(/* shutdown= */ false, /* reason= */ null, /* force= */ false,
241                 /* wipeEuicc= */ false);
242 
243     }
244 
verifyRebootWipeUserData(boolean shutdown, String reason, boolean force, boolean wipeEuicc)245     private void verifyRebootWipeUserData(boolean shutdown, String reason, boolean force,
246             boolean wipeEuicc) throws Exception {
247         boolean called = mRebootWipeUserDataLatch.await(5, TimeUnit.SECONDS);
248         assertWithMessage("rebootWipeUserData not called in 5s").that(called).isTrue();
249 
250         verify(()-> RecoverySystem.rebootWipeUserData(same(mContext), eq(shutdown), eq(reason),
251                 eq(force), eq(wipeEuicc)));
252     }
253 
verifyNoRebootWipeUserData()254     private void verifyNoRebootWipeUserData() {
255         verify(()-> RecoverySystem.rebootWipeUserData(
256                 any(), anyBoolean(), anyString(), anyBoolean(), anyBoolean()), never());
257     }
258 
verifyWipeExternalData()259     private void verifyWipeExternalData() {
260         verify(mSm).wipeAdoptableDisks();
261     }
262 
verifyNoWipeExternalData()263     private void verifyNoWipeExternalData() {
264         verify(mSm, never()).wipeAdoptableDisks();
265     }
266 
verifyWipeNonSystemUser()267     private void verifyWipeNonSystemUser() {
268         verify(mUserManager).removeUserWhenPossible(any(), anyBoolean());
269     }
270 
setPendingResultForUser(int userId)271     private void setPendingResultForUser(int userId) {
272         mReceiver.setPendingResult(new BroadcastReceiver.PendingResult(
273                 Activity.RESULT_OK,
274                 "resultData",
275                 /* resultExtras= */ null,
276                 BroadcastReceiver.PendingResult.TYPE_UNREGISTERED,
277                 /* ordered= */ true,
278                 /* sticky= */ false,
279                 /* token= */ null,
280                 userId,
281                 /* flags= */ 0));
282     }
283 }
284