1 /* 2 * Copyright (C) 2018 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 package com.android.launcher3.util; 17 18 import static androidx.test.InstrumentationRegistry.getContext; 19 import static androidx.test.InstrumentationRegistry.getInstrumentation; 20 import static androidx.test.InstrumentationRegistry.getTargetContext; 21 22 import android.content.pm.LauncherApps; 23 import android.content.res.Resources; 24 import android.os.Handler; 25 import android.os.Looper; 26 import android.os.UserHandle; 27 28 import androidx.test.uiautomator.UiDevice; 29 30 import com.android.launcher3.config.FeatureFlags; 31 import com.android.launcher3.config.FeatureFlags.BooleanFlag; 32 import com.android.launcher3.config.FeatureFlags.IntFlag; 33 34 import org.junit.Assert; 35 36 import java.io.FileOutputStream; 37 import java.io.IOException; 38 import java.io.InputStream; 39 import java.util.concurrent.CountDownLatch; 40 import java.util.function.Predicate; 41 import java.util.function.ToIntFunction; 42 43 public class TestUtil { 44 public static final String DUMMY_PACKAGE = "com.example.android.aardwolf"; 45 installDummyApp()46 public static void installDummyApp() throws IOException { 47 // Copy apk from resources to a local file and install from there. 48 final Resources resources = getContext().getResources(); 49 final InputStream in = resources.openRawResource( 50 resources.getIdentifier("aardwolf_dummy_app", 51 "raw", getContext().getPackageName())); 52 final String apkFilename = getInstrumentation().getTargetContext(). 53 getFilesDir().getPath() + "/dummy_app.apk"; 54 55 try (PackageInstallCheck pic = new PackageInstallCheck()) { 56 final FileOutputStream out = new FileOutputStream(apkFilename); 57 byte[] buff = new byte[1024]; 58 int read; 59 60 while ((read = in.read(buff)) > 0) { 61 out.write(buff, 0, read); 62 } 63 in.close(); 64 out.close(); 65 66 final String result = UiDevice.getInstance(getInstrumentation()) 67 .executeShellCommand("pm install " + apkFilename); 68 Assert.assertTrue( 69 "Failed to install wellbeing test apk; make sure the device is rooted", 70 "Success".equals(result.replaceAll("\\s+", ""))); 71 pic.mAddWait.await(); 72 } catch (InterruptedException e) { 73 throw new IOException(e); 74 } 75 } 76 77 /** 78 * Utility class to override a boolean flag during test. Note that the returned SafeCloseable 79 * must be closed to restore the original state 80 */ overrideFlag(BooleanFlag flag, boolean value)81 public static SafeCloseable overrideFlag(BooleanFlag flag, boolean value) { 82 Predicate<BooleanFlag> originalProxy = FeatureFlags.sBooleanReader; 83 Predicate<BooleanFlag> testProxy = f -> f == flag ? value : originalProxy.test(f); 84 FeatureFlags.sBooleanReader = testProxy; 85 return () -> { 86 if (FeatureFlags.sBooleanReader == testProxy) { 87 FeatureFlags.sBooleanReader = originalProxy; 88 } 89 }; 90 } 91 92 /** 93 * Utility class to override a int flag during test. Note that the returned SafeCloseable 94 * must be closed to restore the original state 95 */ 96 public static SafeCloseable overrideFlag(IntFlag flag, int value) { 97 ToIntFunction<IntFlag> originalProxy = FeatureFlags.sIntReader; 98 ToIntFunction<IntFlag> testProxy = f -> f == flag ? value : originalProxy.applyAsInt(f); 99 FeatureFlags.sIntReader = testProxy; 100 return () -> { 101 if (FeatureFlags.sIntReader == testProxy) { 102 FeatureFlags.sIntReader = originalProxy; 103 } 104 }; 105 } 106 107 public static void uninstallDummyApp() throws IOException { 108 UiDevice.getInstance(getInstrumentation()).executeShellCommand( 109 "pm uninstall " + DUMMY_PACKAGE); 110 } 111 112 private static class PackageInstallCheck extends LauncherApps.Callback 113 implements AutoCloseable { 114 115 final CountDownLatch mAddWait = new CountDownLatch(1); 116 final LauncherApps mLauncherApps; 117 118 PackageInstallCheck() { 119 mLauncherApps = getTargetContext().getSystemService(LauncherApps.class); 120 mLauncherApps.registerCallback(this, new Handler(Looper.getMainLooper())); 121 } 122 123 private void verifyPackage(String packageName) { 124 if (DUMMY_PACKAGE.equals(packageName)) { 125 mAddWait.countDown(); 126 } 127 } 128 129 @Override 130 public void onPackageAdded(String packageName, UserHandle user) { 131 verifyPackage(packageName); 132 } 133 134 @Override 135 public void onPackageChanged(String packageName, UserHandle user) { 136 verifyPackage(packageName); 137 } 138 139 @Override 140 public void onPackageRemoved(String packageName, UserHandle user) { } 141 142 @Override 143 public void onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing) { 144 for (String packageName : packageNames) { 145 verifyPackage(packageName); 146 } 147 } 148 149 @Override 150 public void onPackagesUnavailable(String[] packageNames, UserHandle user, 151 boolean replacing) { } 152 153 @Override 154 public void close() { 155 mLauncherApps.unregisterCallback(this); 156 } 157 } 158 } 159