1 /* 2 * Copyright (C) 2017 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.power.stats; 18 19 import static org.junit.Assert.assertNotNull; 20 import static org.junit.Assert.assertTrue; 21 import static org.junit.Assume.assumeTrue; 22 23 import android.app.ActivityManager; 24 import android.app.IActivityManager; 25 import android.app.IStopUserCallback; 26 import android.content.Context; 27 import android.content.pm.UserInfo; 28 import android.os.RemoteException; 29 import android.os.UserHandle; 30 import android.os.UserManager; 31 import android.platform.test.ravenwood.RavenwoodRule; 32 import android.util.ArraySet; 33 34 import androidx.test.InstrumentationRegistry; 35 import androidx.test.filters.LargeTest; 36 import androidx.test.runner.AndroidJUnit4; 37 import androidx.test.uiautomator.UiDevice; 38 39 import org.junit.After; 40 import org.junit.Before; 41 import org.junit.BeforeClass; 42 import org.junit.Rule; 43 import org.junit.Test; 44 import org.junit.runner.RunWith; 45 46 import java.util.concurrent.CountDownLatch; 47 import java.util.concurrent.TimeUnit; 48 49 @LargeTest 50 @RunWith(AndroidJUnit4.class) 51 @android.platform.test.annotations.DisabledOnRavenwood(reason = "Integration test") 52 public class BatteryStatsUserLifecycleTests { 53 @Rule 54 public final RavenwoodRule mRavenwood = new RavenwoodRule(); 55 56 private static final long POLL_INTERVAL_MS = 500; 57 private static final long USER_REMOVE_TIMEOUT_MS = 5_000; 58 private static final long STOP_USER_TIMEOUT_MS = 20_000; 59 private static final long USER_UIDS_REMOVE_TIMEOUT_MS = 20_000; 60 private static final long BATTERYSTATS_POLLING_TIMEOUT_MS = 5_000; 61 62 private static final String CPU_DATA_TAG = "cpu"; 63 private static final String CPU_FREQ_DATA_TAG = "ctf"; 64 65 private int mTestUserId = UserHandle.USER_NULL; 66 private Context mContext; 67 private UserManager mUm; 68 private IActivityManager mIam; 69 70 @BeforeClass setUpOnce()71 public static void setUpOnce() { 72 if (RavenwoodRule.isOnRavenwood()) { 73 return; 74 } 75 76 assumeTrue(UserManager.getMaxSupportedUsers() > 1); 77 } 78 79 @Before setUp()80 public void setUp() throws Exception { 81 mContext = InstrumentationRegistry.getTargetContext(); 82 mUm = UserManager.get(mContext); 83 mIam = ActivityManager.getService(); 84 final UserInfo user = mUm.createUser("Test_user_" + System.currentTimeMillis() / 1000, 0); 85 assertNotNull("Unable to create test user", user); 86 mTestUserId = user.id; 87 batteryOnScreenOff(); 88 } 89 90 @Test testNoCpuDataForRemovedUser()91 public void testNoCpuDataForRemovedUser() throws Exception { 92 mIam.startUserInBackground(mTestUserId); 93 waitUntilTrue("No uids for started user " + mTestUserId, 94 () -> getNumberOfUidsInBatteryStats() > 0, BATTERYSTATS_POLLING_TIMEOUT_MS); 95 96 final boolean[] userStopped = new boolean[1]; 97 CountDownLatch stopUserLatch = new CountDownLatch(1); 98 mIam.stopUserWithCallback(mTestUserId, new IStopUserCallback.Stub() { 99 @Override 100 public void userStopped(int userId) throws RemoteException { 101 userStopped[0] = true; 102 stopUserLatch.countDown(); 103 } 104 105 @Override 106 public void userStopAborted(int userId) throws RemoteException { 107 stopUserLatch.countDown(); 108 } 109 }); 110 assertTrue("User " + mTestUserId + " could not be stopped in " + STOP_USER_TIMEOUT_MS, 111 stopUserLatch.await(STOP_USER_TIMEOUT_MS, TimeUnit.MILLISECONDS)); 112 assertTrue("User " + mTestUserId + " could not be stopped", userStopped[0]); 113 114 mUm.removeUser(mTestUserId); 115 waitUntilTrue("Unable to remove user " + mTestUserId, () -> { 116 for (UserInfo user : mUm.getUsers()) { 117 if (user.id == mTestUserId) { 118 return false; 119 } 120 } 121 return true; 122 }, USER_REMOVE_TIMEOUT_MS); 123 waitUntilTrue("Uids still found for removed user " + mTestUserId, 124 () -> getNumberOfUidsInBatteryStats() == 0, USER_UIDS_REMOVE_TIMEOUT_MS); 125 } 126 127 @After tearDown()128 public void tearDown() throws Exception { 129 batteryOffScreenOn(); 130 if (mTestUserId != UserHandle.USER_NULL) { 131 mUm.removeUser(mTestUserId); 132 } 133 } 134 getNumberOfUidsInBatteryStats()135 private int getNumberOfUidsInBatteryStats() throws Exception { 136 ArraySet<Integer> uids = new ArraySet<>(); 137 final String dumpsys = executeShellCommand("dumpsys batterystats --checkin"); 138 for (String line : dumpsys.split("\n")) { 139 final String[] parts = line.trim().split(","); 140 if (parts.length < 5 || 141 (!parts[3].equals(CPU_DATA_TAG) && !parts[3].equals(CPU_FREQ_DATA_TAG))) { 142 continue; 143 } 144 try { 145 final int uid = Integer.parseInt(parts[1]); 146 if (UserHandle.getUserId(uid) == mTestUserId) { 147 uids.add(uid); 148 } 149 } catch (NumberFormatException nexc) { 150 // ignore 151 } 152 } 153 return uids.size(); 154 } 155 batteryOnScreenOff()156 protected void batteryOnScreenOff() throws Exception { 157 executeShellCommand("dumpsys battery unplug"); 158 executeShellCommand("dumpsys batterystats enable pretend-screen-off"); 159 } 160 batteryOffScreenOn()161 protected void batteryOffScreenOn() throws Exception { 162 executeShellCommand("dumpsys battery reset"); 163 executeShellCommand("dumpsys batterystats disable pretend-screen-off"); 164 } 165 executeShellCommand(String cmd)166 private String executeShellCommand(String cmd) throws Exception { 167 return UiDevice.getInstance( 168 InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd); 169 } 170 waitUntilTrue(String message, Condition condition, long timeout)171 private void waitUntilTrue(String message, Condition condition, long timeout) throws Exception { 172 final long deadLine = System.currentTimeMillis() + timeout; 173 while (System.currentTimeMillis() <= deadLine && !condition.isTrue()) { 174 Thread.sleep(POLL_INTERVAL_MS); 175 } 176 assertTrue(message, condition.isTrue()); 177 } 178 179 private interface Condition { isTrue()180 boolean isTrue() throws Exception; 181 } 182 } 183