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