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