1 /* 2 * Copyright (C) 2020 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; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertTrue; 21 import static org.mockito.Mockito.when; 22 23 import android.app.ActivityManager; 24 import android.app.IActivityManager; 25 import android.os.Process; 26 import android.os.RemoteException; 27 import android.platform.test.annotations.Presubmit; 28 29 import org.junit.Before; 30 import org.junit.Rule; 31 import org.junit.Test; 32 import org.mockito.Mock; 33 import org.mockito.junit.MockitoJUnit; 34 import org.mockito.junit.MockitoRule; 35 36 import java.io.File; 37 import java.io.IOException; 38 import java.io.PrintWriter; 39 import java.io.StringWriter; 40 import java.nio.charset.StandardCharsets; 41 import java.nio.file.Files; 42 import java.nio.file.Paths; 43 import java.util.ArrayList; 44 import java.util.Arrays; 45 import java.util.List; 46 import java.util.Locale; 47 import java.util.TimeZone; 48 49 /** 50 * Run: atest FrameworksServicesTests:ShutdownCheckPointsTest 51 */ 52 @Presubmit 53 public class ShutdownCheckPointsTest { 54 55 @Rule 56 public MockitoRule rule = MockitoJUnit.rule(); 57 58 @Mock 59 private IActivityManager mActivityManager; 60 61 private TestInjector mTestInjector; 62 private ShutdownCheckPoints mInstance; 63 64 @Before setUp()65 public void setUp() { 66 Locale.setDefault(Locale.UK); 67 TimeZone.setDefault(TimeZone.getTimeZone("UTC")); 68 mTestInjector = new TestInjector(mActivityManager); 69 mInstance = new ShutdownCheckPoints(mTestInjector); 70 } 71 72 @Test testSystemServerEntry()73 public void testSystemServerEntry() { 74 mTestInjector.setCurrentTime(1000); 75 mInstance.recordCheckPointInternal("reason1"); 76 77 assertTrue(dumpToString().startsWith( 78 "Shutdown request from SYSTEM for reason reason1 " 79 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 80 + "com.android.server.power.ShutdownCheckPointsTest" 81 + ".testSystemServerEntry\n at ")); 82 } 83 84 @Test testSystemServerEntryWithoutReason()85 public void testSystemServerEntryWithoutReason() { 86 mTestInjector.setCurrentTime(1000); 87 mInstance.recordCheckPointInternal(null); 88 89 assertTrue(dumpToString().startsWith( 90 "Shutdown request from SYSTEM at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n")); 91 } 92 93 @Test testSystemServiceBinderEntry()94 public void testSystemServiceBinderEntry() { 95 mTestInjector.setCurrentTime(1000); 96 mInstance.recordCheckPointInternal(Process.myPid(), "reason1"); 97 98 assertTrue(dumpToString().startsWith( 99 "Shutdown request from SYSTEM for reason reason1 " 100 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 101 + "com.android.server.power.ShutdownCheckPointsTest" 102 + ".testSystemServiceBinderEntry\n at ")); 103 } 104 105 @Test testCallerProcessBinderEntry()106 public void testCallerProcessBinderEntry() throws RemoteException { 107 List<ActivityManager.RunningAppProcessInfo> runningAppProcessInfos = new ArrayList<>(); 108 runningAppProcessInfos.add( 109 new ActivityManager.RunningAppProcessInfo("process_name", 1, new String[0])); 110 when(mActivityManager.getRunningAppProcesses()).thenReturn(runningAppProcessInfos); 111 112 mTestInjector.setCurrentTime(1000); 113 mInstance.recordCheckPointInternal(1, "reason1"); 114 115 assertEquals( 116 "Shutdown request from BINDER for reason reason1 " 117 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 118 + "com.android.server.power.ShutdownCheckPointsTest" 119 + ".testCallerProcessBinderEntry\n" 120 + "From process process_name (pid=1)\n\n", 121 dumpToString()); 122 } 123 124 @Test testRemoteExceptionOnBinderEntry()125 public void testRemoteExceptionOnBinderEntry() throws RemoteException { 126 when(mActivityManager.getRunningAppProcesses()).thenThrow(new RemoteException("Error")); 127 128 mTestInjector.setCurrentTime(1000); 129 mInstance.recordCheckPointInternal(1, "reason1"); 130 131 assertEquals( 132 "Shutdown request from BINDER for reason reason1 " 133 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 134 + "com.android.server.power.ShutdownCheckPointsTest" 135 + ".testRemoteExceptionOnBinderEntry\n" 136 + "From process ? (pid=1)\n\n", 137 dumpToString()); 138 } 139 140 @Test testUnknownProcessBinderEntry()141 public void testUnknownProcessBinderEntry() { 142 mTestInjector.setCurrentTime(1000); 143 mInstance.recordCheckPointInternal(1, "reason1"); 144 145 assertEquals( 146 "Shutdown request from BINDER for reason reason1 " 147 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 148 + "com.android.server.power.ShutdownCheckPointsTest" 149 + ".testUnknownProcessBinderEntry\n" 150 + "From process ? (pid=1)\n\n", 151 dumpToString()); 152 } 153 154 @Test testBinderEntryWithoutReason()155 public void testBinderEntryWithoutReason() throws RemoteException { 156 mTestInjector.setCurrentTime(1000); 157 mInstance.recordCheckPointInternal(1, null); 158 159 assertTrue(dumpToString().startsWith( 160 "Shutdown request from BINDER at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n")); 161 } 162 163 @Test testSystemServiceIntentEntry()164 public void testSystemServiceIntentEntry() { 165 mTestInjector.setCurrentTime(1000); 166 mInstance.recordCheckPointInternal("some.intent", "android", "reason1"); 167 168 assertTrue(dumpToString().startsWith( 169 "Shutdown request from SYSTEM for reason reason1 " 170 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 171 + "com.android.server.power.ShutdownCheckPointsTest" 172 + ".testSystemServiceIntentEntry\n at ")); 173 } 174 175 @Test testIntentEntry()176 public void testIntentEntry() { 177 mTestInjector.setCurrentTime(1000); 178 mInstance.recordCheckPointInternal("some.intent", "some.app", "reason1"); 179 180 assertEquals( 181 "Shutdown request from INTENT for reason reason1 " 182 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 183 + "Intent: some.intent\n" 184 + "Package: some.app\n\n", 185 dumpToString()); 186 } 187 188 @Test testIntentEntryWithoutReason()189 public void testIntentEntryWithoutReason() { 190 mTestInjector.setCurrentTime(1000); 191 mInstance.recordCheckPointInternal("some.intent", "some.app", null); 192 193 assertTrue(dumpToString().startsWith( 194 "Shutdown request from INTENT at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n")); 195 } 196 197 @Test testMultipleEntries()198 public void testMultipleEntries() { 199 mTestInjector.setCurrentTime(1000); 200 mInstance.recordCheckPointInternal(1, "reason1"); 201 mTestInjector.setCurrentTime(2000); 202 mInstance.recordCheckPointInternal(2, "reason2"); 203 mTestInjector.setCurrentTime(3000); 204 mInstance.recordCheckPointInternal("intent", "app", "reason3"); 205 206 assertEquals( 207 "Shutdown request from BINDER for reason reason1 " 208 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 209 + "com.android.server.power.ShutdownCheckPointsTest.testMultipleEntries\n" 210 + "From process ? (pid=1)\n\n" 211 + "Shutdown request from BINDER for reason reason2 " 212 + "at 1970-01-01 00:00:02.000 UTC (epoch=2000)\n" 213 + "com.android.server.power.ShutdownCheckPointsTest.testMultipleEntries\n" 214 + "From process ? (pid=2)\n\n" 215 + "Shutdown request from INTENT for reason reason3 " 216 + "at 1970-01-01 00:00:03.000 UTC (epoch=3000)\n" 217 + "Intent: intent\n" 218 + "Package: app\n\n", 219 dumpToString()); 220 } 221 222 @Test testTooManyEntriesDropsOlderOnes()223 public void testTooManyEntriesDropsOlderOnes() { 224 mTestInjector.setCheckPointsLimit(2); 225 ShutdownCheckPoints limitedInstance = new ShutdownCheckPoints(mTestInjector); 226 227 mTestInjector.setCurrentTime(1000); 228 limitedInstance.recordCheckPointInternal("intent.1", "app.1", "reason1"); 229 mTestInjector.setCurrentTime(2000); 230 limitedInstance.recordCheckPointInternal("intent.2", "app.2", "reason2"); 231 mTestInjector.setCurrentTime(3000); 232 limitedInstance.recordCheckPointInternal("intent.3", "app.3", "reason3"); 233 234 // Drops first intent. 235 assertEquals( 236 "Shutdown request from INTENT for reason reason2 " 237 + "at 1970-01-01 00:00:02.000 UTC (epoch=2000)\n" 238 + "Intent: intent.2\n" 239 + "Package: app.2\n\n" 240 + "Shutdown request from INTENT for reason reason3 " 241 + "at 1970-01-01 00:00:03.000 UTC (epoch=3000)\n" 242 + "Intent: intent.3\n" 243 + "Package: app.3\n\n", 244 dumpToString(limitedInstance)); 245 } 246 247 @Test testDumpToFile()248 public void testDumpToFile() throws Exception { 249 File tempDir = createTempDir(); 250 File baseFile = new File(tempDir, "checkpoints"); 251 252 mTestInjector.setCurrentTime(1000); 253 mInstance.recordCheckPointInternal("first.intent", "first.app", "reason1"); 254 dumpToFile(baseFile); 255 256 mTestInjector.setCurrentTime(2000); 257 mInstance.recordCheckPointInternal("second.intent", "second.app", "reason2"); 258 dumpToFile(baseFile); 259 260 File[] dumpFiles = tempDir.listFiles(); 261 Arrays.sort(dumpFiles); 262 263 assertEquals(2, dumpFiles.length); 264 assertEquals( 265 "Shutdown request from INTENT for reason reason1 " 266 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 267 + "Intent: first.intent\n" 268 + "Package: first.app\n\n", 269 readFileAsString(dumpFiles[0].getAbsolutePath())); 270 assertEquals( 271 "Shutdown request from INTENT for reason reason1 " 272 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 273 + "Intent: first.intent\n" 274 + "Package: first.app\n\n" 275 + "Shutdown request from INTENT for reason reason2 " 276 + "at 1970-01-01 00:00:02.000 UTC (epoch=2000)\n" 277 + "Intent: second.intent\n" 278 + "Package: second.app\n\n", 279 readFileAsString(dumpFiles[1].getAbsolutePath())); 280 } 281 282 @Test testTooManyFilesDropsOlderOnes()283 public void testTooManyFilesDropsOlderOnes() throws Exception { 284 mTestInjector.setDumpFilesLimit(1); 285 ShutdownCheckPoints instance = new ShutdownCheckPoints(mTestInjector); 286 File tempDir = createTempDir(); 287 File baseFile = new File(tempDir, "checkpoints"); 288 289 mTestInjector.setCurrentTime(1000); 290 instance.recordCheckPointInternal("first.intent", "first.app", "reason1"); 291 dumpToFile(instance, baseFile); 292 293 mTestInjector.setCurrentTime(2000); 294 instance.recordCheckPointInternal("second.intent", "second.app", "reason2"); 295 dumpToFile(instance, baseFile); 296 297 File[] dumpFiles = tempDir.listFiles(); 298 assertEquals(1, dumpFiles.length); 299 assertEquals( 300 "Shutdown request from INTENT for reason reason1 " 301 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 302 + "Intent: first.intent\n" 303 + "Package: first.app\n\n" 304 + "Shutdown request from INTENT for reason reason2 " 305 + "at 1970-01-01 00:00:02.000 UTC (epoch=2000)\n" 306 + "Intent: second.intent\n" 307 + "Package: second.app\n\n", 308 readFileAsString(dumpFiles[0].getAbsolutePath())); 309 } 310 dumpToString()311 private String dumpToString() { 312 return dumpToString(mInstance); 313 } 314 dumpToString(ShutdownCheckPoints instance)315 private String dumpToString(ShutdownCheckPoints instance) { 316 StringWriter sw = new StringWriter(); 317 PrintWriter pw = new PrintWriter(sw); 318 instance.dumpInternal(pw); 319 return sw.toString(); 320 } 321 dumpToFile(File baseFile)322 private void dumpToFile(File baseFile) throws InterruptedException { 323 dumpToFile(mInstance, baseFile); 324 } 325 dumpToFile(ShutdownCheckPoints instance, File baseFile)326 private void dumpToFile(ShutdownCheckPoints instance, File baseFile) 327 throws InterruptedException { 328 Thread dumpThread = instance.newDumpThreadInternal(baseFile); 329 dumpThread.start(); 330 dumpThread.join(); 331 } 332 readFileAsString(String absolutePath)333 private String readFileAsString(String absolutePath) throws IOException { 334 return new String(Files.readAllBytes(Paths.get(absolutePath)), StandardCharsets.UTF_8); 335 } 336 createTempDir()337 private File createTempDir() throws IOException { 338 File tempDir = File.createTempFile("checkpoints", "out"); 339 tempDir.delete(); 340 tempDir.mkdir(); 341 return tempDir; 342 } 343 344 /** Fake system dependencies for testing. */ 345 private final class TestInjector implements ShutdownCheckPoints.Injector { 346 private long mNow; 347 private int mCheckPointsLimit; 348 private int mDumpFilesLimit; 349 private IActivityManager mActivityManager; 350 TestInjector(IActivityManager activityManager)351 TestInjector(IActivityManager activityManager) { 352 mNow = 0; 353 mCheckPointsLimit = 100; 354 mDumpFilesLimit = 2; 355 mActivityManager = activityManager; 356 } 357 358 @Override currentTimeMillis()359 public long currentTimeMillis() { 360 return mNow; 361 } 362 363 @Override maxCheckPoints()364 public int maxCheckPoints() { 365 return mCheckPointsLimit; 366 } 367 368 @Override maxDumpFiles()369 public int maxDumpFiles() { 370 return mDumpFilesLimit; 371 } 372 373 @Override activityManager()374 public IActivityManager activityManager() { 375 return mActivityManager; 376 } 377 setCurrentTime(long time)378 void setCurrentTime(long time) { 379 mNow = time; 380 } 381 setCheckPointsLimit(int limit)382 void setCheckPointsLimit(int limit) { 383 mCheckPointsLimit = limit; 384 } 385 setDumpFilesLimit(int dumpFilesLimit)386 void setDumpFilesLimit(int dumpFilesLimit) { 387 mDumpFilesLimit = dumpFilesLimit; 388 } 389 } 390 } 391