1 /* 2 * Copyright (C) 2021 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.tests.loadingprogress.device; 18 19 import static org.junit.Assert.assertNotNull; 20 import static org.junit.Assert.assertTrue; 21 22 import android.annotation.NonNull; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.pm.ApplicationInfo; 26 import android.content.pm.LauncherActivityInfo; 27 import android.content.pm.LauncherApps; 28 import android.content.pm.PackageManager; 29 import android.os.ConditionVariable; 30 import android.os.Handler; 31 import android.os.HandlerThread; 32 import android.os.Process; 33 import android.os.UserHandle; 34 35 import androidx.test.filters.LargeTest; 36 import androidx.test.platform.app.InstrumentationRegistry; 37 import androidx.test.runner.AndroidJUnit4; 38 39 import org.junit.After; 40 import org.junit.Before; 41 import org.junit.Test; 42 import org.junit.runner.RunWith; 43 44 import java.io.File; 45 import java.nio.file.Files; 46 import java.nio.file.Paths; 47 import java.util.List; 48 import java.util.function.Predicate; 49 50 /** 51 * Device-side test, launched by the host-side test only and should not be called directly. 52 */ 53 @RunWith(AndroidJUnit4.class) 54 @LargeTest 55 public class LoadingProgressTest { 56 private static final String TEST_PACKAGE_NAME = "com.android.tests.loadingprogress.app"; 57 private static final String REGISTER_APP_NAME = "com.android.tests.loadingprogress.registerapp"; 58 59 protected Context mContext; 60 private PackageManager mPackageManager; 61 private UserHandle mUser; 62 private LauncherApps mLauncherApps; 63 private static final int WAIT_TIMEOUT_MILLIS = 2000; /* 2 second */ 64 private ConditionVariable mCalled = new ConditionVariable(); 65 private final HandlerThread mCallbackThread = new HandlerThread("callback"); 66 private LauncherAppsCallback mCallback; 67 68 @Before setUp()69 public void setUp() { 70 mContext = InstrumentationRegistry.getInstrumentation().getContext(); 71 mPackageManager = mContext.getPackageManager(); 72 assertNotNull(mPackageManager); 73 mUser = Process.myUserHandle(); 74 mLauncherApps = mContext.getSystemService(LauncherApps.class); 75 mCallbackThread.start(); 76 } 77 78 @After tearDown()79 public void tearDown() { 80 mCallbackThread.quit(); 81 if (mCallback != null) { 82 mLauncherApps.unregisterCallback(mCallback); 83 } 84 } 85 86 @Test registerFirstLauncherAppsCallback()87 public void registerFirstLauncherAppsCallback() { 88 final Intent intent = mPackageManager.getLaunchIntentForPackage(REGISTER_APP_NAME); 89 assertNotNull(intent); 90 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 91 mContext.startActivity(intent); 92 } 93 94 @Test testGetPartialLoadingProgress()95 public void testGetPartialLoadingProgress() throws Exception { 96 // Package is installed but only partially streamed 97 checkLoadingProgress(loadingProgress -> loadingProgress < 1.0f && loadingProgress > 0); 98 } 99 100 @Test testReadAllBytes()101 public void testReadAllBytes() throws Exception { 102 ApplicationInfo appInfo = mLauncherApps.getApplicationInfo( 103 TEST_PACKAGE_NAME, /* flags= */ 0, mUser); 104 final String codePath = appInfo.sourceDir; 105 final String apkDir = codePath.substring(0, codePath.lastIndexOf('/')); 106 for (String apkName : new File(apkDir).list()) { 107 final String apkPath = apkDir + "/" + apkName; 108 assertTrue(new File(apkPath).exists()); 109 byte[] apkContentBytes = Files.readAllBytes(Paths.get(apkPath)); 110 assertNotNull(apkContentBytes); 111 assertTrue(apkContentBytes.length > 0); 112 } 113 } 114 115 @Test testGetFullLoadingProgress()116 public void testGetFullLoadingProgress() throws Exception { 117 // Package should be fully streamed now 118 checkLoadingProgress(loadingProgress -> (1 - loadingProgress) < 0.001f); 119 } 120 checkLoadingProgress(Predicate<Float> progressCondition)121 private void checkLoadingProgress(Predicate<Float> progressCondition) { 122 List<LauncherActivityInfo> activities = 123 mLauncherApps.getActivityList(TEST_PACKAGE_NAME, mUser); 124 boolean foundTestApp = false; 125 for (LauncherActivityInfo activity : activities) { 126 if (activity.getComponentName().getPackageName().equals( 127 TEST_PACKAGE_NAME)) { 128 foundTestApp = true; 129 final float progress = activity.getLoadingProgress(); 130 assertTrue("progress <" + progress + "> does not meet requirement", 131 progressCondition.test(progress)); 132 } 133 assertTrue(activity.getUser().equals(mUser)); 134 } 135 assertTrue(foundTestApp); 136 } 137 138 @Test testOnPackageLoadingProgressChangedCalledWithPartialLoaded()139 public void testOnPackageLoadingProgressChangedCalledWithPartialLoaded() throws Exception { 140 mCalled.close(); 141 mCallback = new LauncherAppsCallback( 142 loadingProgress -> loadingProgress < 1.0f && loadingProgress > 0); 143 mLauncherApps.registerCallback(mCallback, new Handler(mCallbackThread.getLooper())); 144 assertTrue(mCalled.block(WAIT_TIMEOUT_MILLIS)); 145 } 146 147 @Test testOnPackageLoadingProgressChangedCalledWithFullyLoaded()148 public void testOnPackageLoadingProgressChangedCalledWithFullyLoaded() throws Exception { 149 mCalled.close(); 150 mCallback = new LauncherAppsCallback(loadingProgress -> 1 - loadingProgress < 0.001); 151 mLauncherApps.registerCallback(mCallback, new Handler(mCallbackThread.getLooper())); 152 testReadAllBytes(); 153 assertTrue(mCalled.block(WAIT_TIMEOUT_MILLIS)); 154 } 155 156 class LauncherAppsCallback extends LauncherApps.Callback { 157 private final Predicate<Float> mCondition; LauncherAppsCallback(Predicate<Float> progressCondition)158 LauncherAppsCallback(Predicate<Float> progressCondition) { 159 mCondition = progressCondition; 160 } 161 @Override onPackageRemoved(String packageName, UserHandle user)162 public void onPackageRemoved(String packageName, UserHandle user) { 163 } 164 165 @Override onPackageAdded(String packageName, UserHandle user)166 public void onPackageAdded(String packageName, UserHandle user) { 167 } 168 169 @Override onPackageChanged(String packageName, UserHandle user)170 public void onPackageChanged(String packageName, UserHandle user) { 171 } 172 173 @Override onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing)174 public void onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing) { 175 } 176 177 @Override onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing)178 public void onPackagesUnavailable(String[] packageNames, UserHandle user, 179 boolean replacing) { 180 } 181 182 @Override onPackageLoadingProgressChanged(@onNull String packageName, @NonNull UserHandle user, float progress)183 public void onPackageLoadingProgressChanged(@NonNull String packageName, 184 @NonNull UserHandle user, float progress) { 185 if (mCondition.test(progress)) { 186 // Only release when progress meets the expected condition 187 mCalled.open(); 188 } 189 } 190 } 191 } 192