1 /*
2  * Copyright (C) 2019 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.wallpaper;
18 
19 import static android.app.WallpaperManager.COMMAND_REAPPLY;
20 import static android.app.WallpaperManager.FLAG_LOCK;
21 import static android.app.WallpaperManager.FLAG_SYSTEM;
22 import static android.os.FileObserver.CLOSE_WRITE;
23 import static android.os.UserHandle.MIN_SECONDARY_USER_ID;
24 import static android.os.UserHandle.USER_SYSTEM;
25 import static android.view.Display.DEFAULT_DISPLAY;
26 
27 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
28 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
29 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
30 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
31 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
32 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
33 import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER;
34 
35 import static org.hamcrest.core.IsNot.not;
36 import static org.junit.Assert.assertEquals;
37 import static org.junit.Assert.assertNotEquals;
38 import static org.junit.Assert.assertNotNull;
39 import static org.junit.Assert.assertNull;
40 import static org.junit.Assert.assertTrue;
41 import static org.junit.Assert.fail;
42 import static org.junit.Assume.assumeThat;
43 import static org.mockito.ArgumentMatchers.any;
44 import static org.mockito.ArgumentMatchers.anyBoolean;
45 import static org.mockito.ArgumentMatchers.anyInt;
46 import static org.mockito.ArgumentMatchers.anyLong;
47 import static org.mockito.ArgumentMatchers.eq;
48 import static org.mockito.Mockito.reset;
49 import static org.mockito.Mockito.verify;
50 
51 import android.app.AppGlobals;
52 import android.app.AppOpsManager;
53 import android.app.WallpaperColors;
54 import android.app.WallpaperManager;
55 import android.content.ComponentName;
56 import android.content.Context;
57 import android.content.Intent;
58 import android.content.pm.IPackageManager;
59 import android.content.pm.PackageManager;
60 import android.content.pm.ParceledListSlice;
61 import android.content.pm.ServiceInfo;
62 import android.graphics.Color;
63 import android.hardware.display.DisplayManager;
64 import android.os.ParcelFileDescriptor;
65 import android.os.RemoteException;
66 import android.os.SystemClock;
67 import android.platform.test.annotations.Presubmit;
68 import android.service.wallpaper.IWallpaperConnection;
69 import android.service.wallpaper.IWallpaperEngine;
70 import android.service.wallpaper.WallpaperService;
71 import android.testing.TestableContext;
72 import android.util.Log;
73 import android.util.SparseArray;
74 import android.util.Xml;
75 import android.view.Display;
76 
77 import androidx.test.platform.app.InstrumentationRegistry;
78 import androidx.test.runner.AndroidJUnit4;
79 
80 import com.android.dx.mockito.inline.extended.ExtendedMockito;
81 import com.android.dx.mockito.inline.extended.StaticMockitoSession;
82 import com.android.internal.R;
83 import com.android.modules.utils.TypedXmlPullParser;
84 import com.android.modules.utils.TypedXmlSerializer;
85 import com.android.server.LocalServices;
86 import com.android.server.wm.WindowManagerInternal;
87 
88 import org.hamcrest.CoreMatchers;
89 import org.junit.After;
90 import org.junit.AfterClass;
91 import org.junit.Before;
92 import org.junit.BeforeClass;
93 import org.junit.ClassRule;
94 import org.junit.Rule;
95 import org.junit.Test;
96 import org.junit.rules.TemporaryFolder;
97 import org.junit.runner.RunWith;
98 import org.mockito.Mock;
99 import org.mockito.MockitoAnnotations;
100 import org.mockito.quality.Strictness;
101 import org.xmlpull.v1.XmlPullParserException;
102 
103 import java.io.ByteArrayOutputStream;
104 import java.io.File;
105 import java.io.FileInputStream;
106 import java.io.FileOutputStream;
107 import java.io.IOException;
108 import java.nio.charset.StandardCharsets;
109 import java.util.List;
110 
111 /**
112  * Tests for the {@link WallpaperManagerService} class.
113  *
114  * Build/Install/Run:
115  * atest FrameworksMockingServicesTests:WallpaperManagerServiceTests
116  */
117 @Presubmit
118 @RunWith(AndroidJUnit4.class)
119 public class WallpaperManagerServiceTests {
120 
121     private static final String TAG = "WallpaperManagerServiceTests";
122     private static final int DISPLAY_SIZE_DIMENSION = 100;
123     private static StaticMockitoSession sMockitoSession;
124 
125     @ClassRule
126     public static final TestableContext sContext = new TestableContext(
127             InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
128     private static ComponentName sImageWallpaperComponentName;
129     private static ComponentName sDefaultWallpaperComponent;
130 
131     private IPackageManager mIpm = AppGlobals.getPackageManager();
132 
133     @Mock
134     private DisplayManager mDisplayManager;
135 
136     @Rule
137     public final TemporaryFolder mFolder = new TemporaryFolder();
138     private final SparseArray<File> mTempDirs = new SparseArray<>();
139     private WallpaperManagerService mService;
140     private static IWallpaperConnection.Stub sWallpaperService;
141 
142     @BeforeClass
setUpClass()143     public static void setUpClass() {
144         sMockitoSession = mockitoSession()
145                 .strictness(Strictness.LENIENT)
146                 .spyStatic(WallpaperUtils.class)
147                 .spyStatic(LocalServices.class)
148                 .spyStatic(WallpaperManager.class)
149                 .startMocking();
150 
151         final WindowManagerInternal dmi = mock(WindowManagerInternal.class);
152         LocalServices.addService(WindowManagerInternal.class, dmi);
153 
154         sContext.addMockSystemService(Context.APP_OPS_SERVICE, mock(AppOpsManager.class));
155 
156         spyOn(sContext);
157         sContext.getTestablePermissions().setPermission(
158                 android.Manifest.permission.SET_WALLPAPER_COMPONENT,
159                 PackageManager.PERMISSION_GRANTED);
160         sContext.getTestablePermissions().setPermission(
161                 android.Manifest.permission.SET_WALLPAPER,
162                 PackageManager.PERMISSION_GRANTED);
163         sContext.getTestablePermissions().setPermission(
164                 android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT,
165                 PackageManager.PERMISSION_GRANTED);
166         sContext.getTestablePermissions().setPermission(
167                 android.Manifest.permission.READ_WALLPAPER_INTERNAL,
168                 PackageManager.PERMISSION_GRANTED);
169         doNothing().when(sContext).sendBroadcastAsUser(any(), any());
170 
171         //Wallpaper components
172         sWallpaperService = mock(IWallpaperConnection.Stub.class);
173         sImageWallpaperComponentName = ComponentName.unflattenFromString(
174                 sContext.getResources().getString(R.string.image_wallpaper_component));
175         // Mock default wallpaper as image wallpaper if there is no pre-defined default wallpaper.
176         sDefaultWallpaperComponent = WallpaperManager.getCmfDefaultWallpaperComponent(sContext);
177 
178         if (sDefaultWallpaperComponent == null) {
179             sDefaultWallpaperComponent = sImageWallpaperComponentName;
180             doReturn(sImageWallpaperComponentName).when(() ->
181                     WallpaperManager.getCmfDefaultWallpaperComponent(any()));
182         } else {
183             sContext.addMockService(sDefaultWallpaperComponent, sWallpaperService);
184         }
185 
186         sContext.addMockService(sImageWallpaperComponentName, sWallpaperService);
187     }
188 
189     @AfterClass
tearDownClass()190     public static void tearDownClass() {
191         if (sMockitoSession != null) {
192             sMockitoSession.finishMocking();
193             sMockitoSession = null;
194         }
195         LocalServices.removeServiceForTest(WindowManagerInternal.class);
196         sImageWallpaperComponentName = null;
197         sDefaultWallpaperComponent = null;
198         reset(sContext);
199     }
200 
201     @Before
setUp()202     public void setUp() {
203         MockitoAnnotations.initMocks(this);
204         ExtendedMockito.doAnswer(invocation -> {
205             int userId = (invocation.getArgument(0));
206             return getWallpaperTestDir(userId);
207         }).when(() -> WallpaperUtils.getWallpaperDir(anyInt()));
208 
209         sContext.addMockSystemService(DisplayManager.class, mDisplayManager);
210 
211         final Display mockDisplay = mock(Display.class);
212         doReturn(DISPLAY_SIZE_DIMENSION).when(mockDisplay).getMaximumSizeDimension();
213         doReturn(mockDisplay).when(mDisplayManager).getDisplay(anyInt());
214 
215         final Display[] displays = new Display[]{mockDisplay};
216         doReturn(displays).when(mDisplayManager).getDisplays();
217 
218         spyOn(mIpm);
219         mService = new TestWallpaperManagerService(sContext);
220         spyOn(mService);
221         mService.systemReady();
222     }
223 
224     @After
tearDown()225     public void tearDown() {
226         LocalServices.removeServiceForTest(WallpaperManagerInternal.class);
227 
228         mTempDirs.clear();
229         reset(mIpm);
230         mService = null;
231     }
232 
getWallpaperTestDir(int userId)233     private File getWallpaperTestDir(int userId) {
234         File tempDir = mTempDirs.get(userId);
235         if (tempDir == null) {
236             try {
237                 tempDir = mFolder.newFolder(String.valueOf(userId));
238                 mTempDirs.append(userId, tempDir);
239             } catch (IOException e) {
240                 Log.e(TAG, "getWallpaperTestDir failed at userId= " + userId);
241             }
242         }
243         return tempDir;
244     }
245 
246     protected class TestWallpaperManagerService extends WallpaperManagerService {
247 
TestWallpaperManagerService(Context context)248         TestWallpaperManagerService(Context context) {
249             super(context);
250         }
251 
252         // Always return true for test
253         @Override
isWallpaperSupported(String callingPackage)254         public boolean isWallpaperSupported(String callingPackage) {
255             return true;
256         }
257 
258         // Always return true for test
259         @Override
isSetWallpaperAllowed(String callingPackage)260         public boolean isSetWallpaperAllowed(String callingPackage) {
261             return true;
262         }
263     }
264 
265     /**
266      * Tests that the fundamental fields are set by the main WallpaperData constructor
267      */
268     @Test
testWallpaperDataConstructor()269     public void testWallpaperDataConstructor() {
270         final int testUserId = MIN_SECONDARY_USER_ID;
271         for (int which: List.of(FLAG_LOCK, FLAG_SYSTEM)) {
272             WallpaperData newWallpaperData = new WallpaperData(testUserId, which);
273             assertEquals(which, newWallpaperData.mWhich);
274             assertEquals(testUserId, newWallpaperData.userId);
275 
276             WallpaperData wallpaperData = mService.getWallpaperSafeLocked(testUserId, which);
277             assertEquals(wallpaperData.getCropFile().getAbsolutePath(),
278                     newWallpaperData.getCropFile().getAbsolutePath());
279             assertEquals(wallpaperData.getWallpaperFile().getAbsolutePath(),
280                     newWallpaperData.getWallpaperFile().getAbsolutePath());
281         }
282     }
283 
284     /**
285      * Tests that internal basic data should be correct after boot up.
286      */
287     @Test
testDataCorrectAfterBoot()288     public void testDataCorrectAfterBoot() {
289         mService.switchUser(USER_SYSTEM, null);
290 
291         final WallpaperData fallbackData = mService.mFallbackWallpaper;
292         assertEquals("Fallback wallpaper component should be ImageWallpaper.",
293                 sImageWallpaperComponentName, fallbackData.wallpaperComponent);
294 
295         verifyLastWallpaperData(USER_SYSTEM, sDefaultWallpaperComponent);
296         verifyDisplayData();
297     }
298 
299     /**
300      * Tests setWallpaperComponent and clearWallpaper should work as expected.
301      */
302     @Test
testSetThenClearComponent()303     public void testSetThenClearComponent() {
304         // Skip if there is no pre-defined default wallpaper component.
305         assumeThat(sDefaultWallpaperComponent,
306                 not(CoreMatchers.equalTo(sImageWallpaperComponentName)));
307 
308         final int testUserId = USER_SYSTEM;
309         mService.switchUser(testUserId, null);
310         verifyLastWallpaperData(testUserId, sDefaultWallpaperComponent);
311         verifyCurrentSystemData(testUserId);
312 
313         mService.setWallpaperComponent(sImageWallpaperComponentName, sContext.getOpPackageName(),
314                 FLAG_SYSTEM, testUserId);
315         verifyLastWallpaperData(testUserId, sImageWallpaperComponentName);
316         verifyCurrentSystemData(testUserId);
317 
318         mService.clearWallpaper(null, FLAG_SYSTEM, testUserId);
319         verifyLastWallpaperData(testUserId, sDefaultWallpaperComponent);
320         verifyCurrentSystemData(testUserId);
321     }
322 
323     /**
324      * Tests that when setWallpaperComponent is called with the currently set component, a command
325      * is issued to the wallpaper.
326      */
327     @Test
testSetCurrentComponent()328     public void testSetCurrentComponent() throws Exception {
329         final int testUserId = USER_SYSTEM;
330         mService.switchUser(testUserId, null);
331         verifyLastWallpaperData(testUserId, sDefaultWallpaperComponent);
332         verifyCurrentSystemData(testUserId);
333 
334         spyOn(mService.mWallpaperDisplayHelper);
335         doReturn(true).when(mService.mWallpaperDisplayHelper)
336                 .isUsableDisplay(any(Display.class),
337                         eq(mService.mLastWallpaper.connection.mClientUid));
338         mService.mLastWallpaper.connection.attachEngine(mock(IWallpaperEngine.class),
339                 DEFAULT_DISPLAY);
340 
341         WallpaperManagerService.DisplayConnector connector =
342                 mService.mLastWallpaper.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY);
343         mService.setWallpaperComponent(sDefaultWallpaperComponent, sContext.getOpPackageName(),
344                 FLAG_SYSTEM, testUserId);
345 
346         verify(connector.mEngine).dispatchWallpaperCommand(
347                 eq(COMMAND_REAPPLY), anyInt(), anyInt(), anyInt(), any());
348     }
349 
350     /**
351      * Tests internal data should be correct and no crash after switch user continuously.
352      */
353     @Test
testSwitchMultipleUsers()354     public void testSwitchMultipleUsers() throws Exception {
355         final int lastUserId = 5;
356         final ServiceInfo pi = mIpm.getServiceInfo(sDefaultWallpaperComponent,
357                 PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, 0);
358         doReturn(pi).when(mIpm).getServiceInfo(any(), anyLong(), anyInt());
359 
360         final Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
361         final ParceledListSlice ris =
362                 mIpm.queryIntentServices(intent,
363                         intent.resolveTypeIfNeeded(sContext.getContentResolver()),
364                         PackageManager.GET_META_DATA, 0);
365         doReturn(ris).when(mIpm).queryIntentServices(any(), any(), anyLong(), anyInt());
366         doReturn(PackageManager.PERMISSION_GRANTED).when(mIpm).checkPermission(
367                 eq(android.Manifest.permission.AMBIENT_WALLPAPER), any(), anyInt());
368 
369         for (int userId = 0; userId <= lastUserId; userId++) {
370             mService.switchUser(userId, null);
371             verifyLastWallpaperData(userId, sDefaultWallpaperComponent);
372             verifyCurrentSystemData(userId);
373         }
374         verifyNoConnectionBeforeLastUser(lastUserId);
375     }
376 
377     /**
378      * Tests internal data should be correct and no crash after switch user + unlock user
379      * continuously.
380      * Simulating that the selected WallpaperService is not built-in. After switching users, the
381      * service should not be bound, but bound to the image wallpaper. After receiving the user
382      * unlock callback and can find the selected service for the user, the selected service should
383      * be bound.
384      */
385     @Test
testSwitchThenUnlockMultipleUsers()386     public void testSwitchThenUnlockMultipleUsers() throws Exception {
387         final int lastUserId = 5;
388         final ServiceInfo pi = mIpm.getServiceInfo(sDefaultWallpaperComponent,
389                 PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, 0);
390         doReturn(pi).when(mIpm).getServiceInfo(any(), anyLong(), anyInt());
391 
392         final Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
393         final ParceledListSlice ris =
394                 mIpm.queryIntentServices(intent,
395                         intent.resolveTypeIfNeeded(sContext.getContentResolver()),
396                         PackageManager.GET_META_DATA, 0);
397         doReturn(PackageManager.PERMISSION_GRANTED).when(mIpm).checkPermission(
398                 eq(android.Manifest.permission.AMBIENT_WALLPAPER), any(), anyInt());
399 
400         for (int userId = 1; userId <= lastUserId; userId++) {
401             mService.switchUser(userId, null);
402             verifyLastWallpaperData(userId, sImageWallpaperComponentName);
403             // Simulate user unlocked
404             doReturn(ris).when(mIpm).queryIntentServices(any(), any(), anyLong(), eq(userId));
405             mService.onUnlockUser(userId);
406             verifyLastWallpaperData(userId, sDefaultWallpaperComponent);
407             verifyCurrentSystemData(userId);
408         }
409         verifyNoConnectionBeforeLastUser(lastUserId);
410         verifyDisplayData();
411     }
412 
413     @Test
testXmlSerializationRoundtrip()414     public void testXmlSerializationRoundtrip() {
415         WallpaperData systemWallpaperData = mService.getCurrentWallpaperData(FLAG_SYSTEM, 0);
416         try {
417             TypedXmlSerializer serializer = Xml.newBinarySerializer();
418             serializer.setOutput(new ByteArrayOutputStream(), StandardCharsets.UTF_8.name());
419             serializer.startDocument(StandardCharsets.UTF_8.name(), true);
420             mService.mWallpaperDataParser.writeWallpaperAttributes(
421                     serializer, "wp", systemWallpaperData);
422         } catch (IOException e) {
423             fail("exception occurred while writing system wallpaper attributes");
424         }
425 
426         WallpaperData shouldMatchSystem = new WallpaperData(0, FLAG_SYSTEM);
427         try {
428             TypedXmlPullParser parser = Xml.newBinaryPullParser();
429             mService.mWallpaperDataParser.parseWallpaperAttributes(parser, shouldMatchSystem, true);
430         } catch (XmlPullParserException e) {
431             fail("exception occurred while parsing wallpaper");
432         }
433         assertEquals(systemWallpaperData.primaryColors, shouldMatchSystem.primaryColors);
434     }
435 
436     @Test
testWallpaperManagerCallbackInRightOrder()437     public void testWallpaperManagerCallbackInRightOrder() throws RemoteException {
438         WallpaperData wallpaper = new WallpaperData(USER_SYSTEM, FLAG_SYSTEM);
439         wallpaper.primaryColors = new WallpaperColors(Color.valueOf(Color.RED),
440                 Color.valueOf(Color.BLUE), null);
441 
442         spyOn(wallpaper);
443         doReturn(wallpaper).when(mService).getWallpaperSafeLocked(wallpaper.userId, FLAG_SYSTEM);
444         doNothing().when(mService).switchWallpaper(any(), any());
445         doReturn(true).when(mService)
446                 .bindWallpaperComponentLocked(any(), anyBoolean(), anyBoolean(), any(), any());
447         doNothing().when(mService).saveSettingsLocked(wallpaper.userId);
448         spyOn(mService.mWallpaperCropper);
449         doNothing().when(mService.mWallpaperCropper).generateCrop(wallpaper);
450 
451         // timestamps of {ACTION_WALLPAPER_CHANGED, onWallpaperColorsChanged}
452         final long[] timestamps = new long[2];
453         doAnswer(invocation -> timestamps[0] = SystemClock.elapsedRealtime())
454                 .when(sContext).sendBroadcastAsUser(any(), any());
455         doAnswer(invocation -> timestamps[1] = SystemClock.elapsedRealtime())
456                 .when(mService).notifyWallpaperColorsChanged(wallpaper, FLAG_SYSTEM);
457 
458         assertNull(wallpaper.wallpaperObserver);
459         mService.switchUser(wallpaper.userId, null);
460         assertNotNull(wallpaper.wallpaperObserver);
461         // We will call onEvent directly, so stop watching the file.
462         wallpaper.wallpaperObserver.stopWatching();
463 
464         spyOn(wallpaper.wallpaperObserver);
465         doReturn(wallpaper).when(wallpaper.wallpaperObserver).dataForEvent(true, false);
466         wallpaper.wallpaperObserver.onEvent(CLOSE_WRITE, WALLPAPER);
467 
468         // ACTION_WALLPAPER_CHANGED should be invoked before onWallpaperColorsChanged.
469         assertTrue(timestamps[1] > timestamps[0]);
470     }
471 
472     @Test
testSetWallpaperDimAmount()473     public void testSetWallpaperDimAmount() throws RemoteException {
474         mService.switchUser(USER_SYSTEM, null);
475         float dimAmount = 0.7f;
476         mService.setWallpaperDimAmount(dimAmount);
477         assertEquals("Getting dim amount should match after setting the dim amount",
478                 mService.getWallpaperDimAmount(), dimAmount, 0.0);
479     }
480 
481     @Test
testGetAdjustedWallpaperColorsOnDimming()482     public void testGetAdjustedWallpaperColorsOnDimming() throws RemoteException {
483         final int testUserId = USER_SYSTEM;
484         mService.switchUser(testUserId, null);
485         mService.setWallpaperComponent(sDefaultWallpaperComponent, sContext.getOpPackageName(),
486                 FLAG_SYSTEM, testUserId);
487         WallpaperData wallpaper = mService.getCurrentWallpaperData(FLAG_SYSTEM, testUserId);
488 
489         // Mock a wallpaper data with color hints that support dark text and dark theme
490         // but not HINT_FROM_BITMAP
491         wallpaper.primaryColors = new WallpaperColors(Color.valueOf(Color.WHITE), null, null,
492                 WallpaperColors.HINT_SUPPORTS_DARK_TEXT | WallpaperColors.HINT_SUPPORTS_DARK_THEME);
493         mService.setWallpaperDimAmount(0.6f);
494         int colorHints = mService.getAdjustedWallpaperColorsOnDimming(wallpaper).getColorHints();
495         // Dimmed wallpaper not extracted from bitmap does not support dark text and dark theme
496         assertNotEquals(WallpaperColors.HINT_SUPPORTS_DARK_TEXT,
497                 colorHints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT);
498         assertNotEquals(WallpaperColors.HINT_SUPPORTS_DARK_THEME,
499                 colorHints & WallpaperColors.HINT_SUPPORTS_DARK_THEME);
500 
501         // Remove dimming
502         mService.setWallpaperDimAmount(0f);
503         colorHints = mService.getAdjustedWallpaperColorsOnDimming(wallpaper).getColorHints();
504         // Undimmed wallpaper not extracted from bitmap does support dark text and dark theme
505         assertEquals(WallpaperColors.HINT_SUPPORTS_DARK_TEXT,
506                 colorHints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT);
507         assertEquals(WallpaperColors.HINT_SUPPORTS_DARK_THEME,
508                 colorHints & WallpaperColors.HINT_SUPPORTS_DARK_THEME);
509 
510         // Mock a wallpaper data with color hints that support dark text and dark theme
511         // and was extracted from bitmap
512         wallpaper.primaryColors = new WallpaperColors(Color.valueOf(Color.WHITE), null, null,
513                 WallpaperColors.HINT_SUPPORTS_DARK_TEXT | WallpaperColors.HINT_SUPPORTS_DARK_THEME
514                         | WallpaperColors.HINT_FROM_BITMAP);
515         mService.setWallpaperDimAmount(0.6f);
516         colorHints = mService.getAdjustedWallpaperColorsOnDimming(wallpaper).getColorHints();
517         // Dimmed wallpaper should still support dark text and dark theme
518         assertEquals(WallpaperColors.HINT_SUPPORTS_DARK_TEXT,
519                 colorHints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT);
520         assertEquals(WallpaperColors.HINT_SUPPORTS_DARK_THEME,
521                 colorHints & WallpaperColors.HINT_SUPPORTS_DARK_THEME);
522     }
523 
524     @Test
getWallpaperWithFeature_getCropped_returnsCropFile()525     public void getWallpaperWithFeature_getCropped_returnsCropFile() throws Exception {
526         File cropSystemWallpaperFile =
527                 new WallpaperData(USER_SYSTEM, FLAG_SYSTEM).getCropFile();
528         cropSystemWallpaperFile.getParentFile().mkdirs();
529         cropSystemWallpaperFile.createNewFile();
530         try (FileOutputStream outputStream = new FileOutputStream(cropSystemWallpaperFile)) {
531             outputStream.write("Crop system wallpaper".getBytes());
532         }
533 
534         ParcelFileDescriptor pfd =
535                 mService.getWallpaperWithFeature(
536                         sContext.getPackageName(),
537                         sContext.getAttributionTag(),
538                         /* cb= */ null,
539                         FLAG_SYSTEM,
540                         /* outParams= */ null,
541                         USER_SYSTEM,
542                         /* getCropped= */ true);
543 
544         assertPfdAndFileContentsEqual(pfd, cropSystemWallpaperFile);
545     }
546 
547     @Test
getWallpaperWithFeature_notGetCropped_returnsOriginalFile()548     public void getWallpaperWithFeature_notGetCropped_returnsOriginalFile() throws Exception {
549         File originalSystemWallpaperFile =
550                 new WallpaperData(USER_SYSTEM, FLAG_SYSTEM).getWallpaperFile();
551         originalSystemWallpaperFile.getParentFile().mkdirs();
552         originalSystemWallpaperFile.createNewFile();
553         try (FileOutputStream outputStream = new FileOutputStream(originalSystemWallpaperFile)) {
554             outputStream.write("Original system wallpaper".getBytes());
555         }
556 
557         ParcelFileDescriptor pfd =
558                 mService.getWallpaperWithFeature(
559                         sContext.getPackageName(),
560                         sContext.getAttributionTag(),
561                         /* cb= */ null,
562                         FLAG_SYSTEM,
563                         /* outParams= */ null,
564                         USER_SYSTEM,
565                         /* getCropped= */ false);
566 
567         assertPfdAndFileContentsEqual(pfd, originalSystemWallpaperFile);
568     }
569 
570     // Verify that after continue switch user from userId 0 to lastUserId, the wallpaper data for
571     // non-current user must not bind to wallpaper service.
verifyNoConnectionBeforeLastUser(int lastUserId)572     private void verifyNoConnectionBeforeLastUser(int lastUserId) {
573         for (int i = 0; i < lastUserId; i++) {
574             final WallpaperData userData = mService.getCurrentWallpaperData(FLAG_SYSTEM, i);
575             assertNull("No user data connection left", userData.connection);
576         }
577     }
578 
verifyLastWallpaperData(int lastUserId, ComponentName expectedComponent)579     private void verifyLastWallpaperData(int lastUserId, ComponentName expectedComponent) {
580         final WallpaperData lastData = mService.mLastWallpaper;
581         assertNotNull("Last wallpaper must not be null", lastData);
582         assertEquals("Last wallpaper component must be equals.", expectedComponent,
583                 lastData.wallpaperComponent);
584         assertEquals("The user id in last wallpaper should be the last switched user",
585                 lastUserId, lastData.userId);
586         assertNotNull("Must exist user data connection on last wallpaper data",
587                 lastData.connection);
588     }
589 
verifyCurrentSystemData(int userId)590     private void verifyCurrentSystemData(int userId) {
591         final WallpaperData lastData = mService.mLastWallpaper;
592         final WallpaperData wallpaper = mService.getCurrentWallpaperData(FLAG_SYSTEM, userId);
593         assertEquals("Last wallpaper should be equals to current system wallpaper",
594                 lastData, wallpaper);
595     }
596 
verifyDisplayData()597     private void verifyDisplayData() {
598         mService.mWallpaperDisplayHelper.forEachDisplayData(data -> {
599             assertTrue("Display width must larger than maximum screen size",
600                     data.mWidth >= DISPLAY_SIZE_DIMENSION);
601             assertTrue("Display height must larger than maximum screen size",
602                     data.mHeight >= DISPLAY_SIZE_DIMENSION);
603         });
604     }
605 
606     /**
607      * Asserts that the contents of the given {@link ParcelFileDescriptor} and {@link File} contain
608      * exactly the same bytes.
609      *
610      * Both the PFD and File contents will be loaded to memory. The PFD will be closed at the end.
611      */
assertPfdAndFileContentsEqual(ParcelFileDescriptor pfd, File file)612     private static void assertPfdAndFileContentsEqual(ParcelFileDescriptor pfd, File file)
613             throws IOException {
614         try (ParcelFileDescriptor.AutoCloseInputStream pfdInputStream =
615                      new ParcelFileDescriptor.AutoCloseInputStream(pfd);
616              FileInputStream fileInputStream = new FileInputStream(file)
617         ) {
618             String pfdContents = new String(pfdInputStream.readAllBytes());
619             String fileContents = new String(fileInputStream.readAllBytes());
620             assertEquals(pfdContents, fileContents);
621         }
622     }
623 }
624