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 testNoCpuDataForRemovedUser()81 public void testNoCpuDataForRemovedUser() throws Exception { 82 mIam.startUserInBackground(mTestUserId); 83 waitUntilTrue("No uids for started user " + mTestUserId, 84 () -> getNumberOfUidsInBatteryStats() > 0, BATTERYSTATS_POLLING_TIMEOUT_MS); 85 86 CountDownLatch stopUserLatch = new CountDownLatch(1); 87 mIam.stopUser(mTestUserId, true, new IStopUserCallback.Stub() { 88 @Override 89 public void userStopped(int userId) throws RemoteException { 90 stopUserLatch.countDown(); 91 } 92 93 @Override 94 public void userStopAborted(int userId) throws RemoteException { 95 } 96 }); 97 assertTrue("User " + mTestUserId + " could not be stopped", 98 stopUserLatch.await(STOP_USER_TIMEOUT_MS, TimeUnit.MILLISECONDS)); 99 100 mUm.removeUser(mTestUserId); 101 waitUntilTrue("Unable to remove user " + mTestUserId, () -> { 102 for (UserInfo user : mUm.getUsers()) { 103 if (user.id == mTestUserId) { 104 return false; 105 } 106 } 107 return true; 108 }, USER_REMOVE_TIMEOUT_MS); 109 waitUntilTrue("Uids still found for removed user " + mTestUserId, 110 () -> getNumberOfUidsInBatteryStats() == 0, BATTERYSTATS_POLLING_TIMEOUT_MS); 111 } 112 113 @After tearDown()114 public void tearDown() throws Exception { 115 batteryOffScreenOn(); 116 if (mTestUserId != UserHandle.USER_NULL) { 117 mUm.removeUser(mTestUserId); 118 } 119 } 120 getNumberOfUidsInBatteryStats()121 private int getNumberOfUidsInBatteryStats() throws Exception { 122 ArraySet<Integer> uids = new ArraySet<>(); 123 final String dumpsys = executeShellCommand("dumpsys batterystats --checkin"); 124 for (String line : dumpsys.split("\n")) { 125 final String[] parts = line.trim().split(","); 126 if (parts.length < 5 || 127 (!parts[3].equals(CPU_DATA_TAG) && !parts[3].equals(CPU_FREQ_DATA_TAG))) { 128 continue; 129 } 130 try { 131 final int uid = Integer.parseInt(parts[1]); 132 if (UserHandle.getUserId(uid) == mTestUserId) { 133 uids.add(uid); 134 } 135 } catch (NumberFormatException nexc) { 136 // ignore 137 } 138 } 139 return uids.size(); 140 } 141 batteryOnScreenOff()142 protected void batteryOnScreenOff() throws Exception { 143 executeShellCommand("dumpsys battery unplug"); 144 executeShellCommand("dumpsys batterystats enable pretend-screen-off"); 145 } 146 batteryOffScreenOn()147 protected void batteryOffScreenOn() throws Exception { 148 executeShellCommand("dumpsys battery reset"); 149 executeShellCommand("dumpsys batterystats disable pretend-screen-off"); 150 } 151 executeShellCommand(String cmd)152 private String executeShellCommand(String cmd) throws Exception { 153 return UiDevice.getInstance( 154 InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd); 155 } 156 waitUntilTrue(String message, Condition condition, long timeout)157 private void waitUntilTrue(String message, Condition condition, long timeout) throws Exception { 158 final long deadLine = System.currentTimeMillis() + timeout; 159 while (System.currentTimeMillis() <= deadLine && !condition.isTrue()) { 160 Thread.sleep(POLL_INTERVAL_MS); 161 } 162 assertTrue(message, condition.isTrue()); 163 } 164 165 private interface Condition { isTrue()166 boolean isTrue() throws Exception; 167 } 168 } 169