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 testCallerProcessBinderEntries()106 public void testCallerProcessBinderEntries() 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 // Matching pid in getRunningAppProcesses 114 mInstance.recordCheckPointInternal(1, "reason1"); 115 // Missing pid in getRunningAppProcesses 116 mInstance.recordCheckPointInternal(2, "reason2"); 117 118 assertEquals( 119 "Shutdown request from BINDER for reason reason1 " 120 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 121 + "com.android.server.power.ShutdownCheckPointsTest" 122 + ".testCallerProcessBinderEntries\n" 123 + "From process process_name (pid=1)\n\n" 124 + "Shutdown request from BINDER for reason reason2 " 125 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 126 + "com.android.server.power.ShutdownCheckPointsTest" 127 + ".testCallerProcessBinderEntries\n" 128 + "From process ? (pid=2)\n\n", 129 dumpToString()); 130 } 131 132 @Test testNullCallerProcessBinderEntries()133 public void testNullCallerProcessBinderEntries() throws RemoteException { 134 when(mActivityManager.getRunningAppProcesses()).thenReturn(null); 135 136 mTestInjector.setCurrentTime(1000); 137 mInstance.recordCheckPointInternal(1, "reason1"); 138 139 assertEquals( 140 "Shutdown request from BINDER for reason reason1 " 141 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 142 + "com.android.server.power.ShutdownCheckPointsTest" 143 + ".testNullCallerProcessBinderEntries\n" 144 + "From process ? (pid=1)\n\n", 145 dumpToString()); 146 } 147 148 @Test testRemoteExceptionOnBinderEntry()149 public void testRemoteExceptionOnBinderEntry() throws RemoteException { 150 when(mActivityManager.getRunningAppProcesses()).thenThrow(new RemoteException("Error")); 151 152 mTestInjector.setCurrentTime(1000); 153 mInstance.recordCheckPointInternal(1, "reason1"); 154 155 assertEquals( 156 "Shutdown request from BINDER for reason reason1 " 157 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 158 + "com.android.server.power.ShutdownCheckPointsTest" 159 + ".testRemoteExceptionOnBinderEntry\n" 160 + "From process ? (pid=1)\n\n", 161 dumpToString()); 162 } 163 164 @Test testUnknownProcessBinderEntry()165 public void testUnknownProcessBinderEntry() { 166 mTestInjector.setCurrentTime(1000); 167 mInstance.recordCheckPointInternal(1, "reason1"); 168 169 assertEquals( 170 "Shutdown request from BINDER for reason reason1 " 171 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 172 + "com.android.server.power.ShutdownCheckPointsTest" 173 + ".testUnknownProcessBinderEntry\n" 174 + "From process ? (pid=1)\n\n", 175 dumpToString()); 176 } 177 178 @Test testBinderEntryWithoutReason()179 public void testBinderEntryWithoutReason() throws RemoteException { 180 mTestInjector.setCurrentTime(1000); 181 mInstance.recordCheckPointInternal(1, null); 182 183 assertTrue(dumpToString().startsWith( 184 "Shutdown request from BINDER at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n")); 185 } 186 187 @Test testSystemServiceIntentEntry()188 public void testSystemServiceIntentEntry() { 189 mTestInjector.setCurrentTime(1000); 190 mInstance.recordCheckPointInternal("some.intent", "android", "reason1"); 191 192 assertTrue(dumpToString().startsWith( 193 "Shutdown request from SYSTEM for reason reason1 " 194 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 195 + "com.android.server.power.ShutdownCheckPointsTest" 196 + ".testSystemServiceIntentEntry\n at ")); 197 } 198 199 @Test testIntentEntry()200 public void testIntentEntry() { 201 mTestInjector.setCurrentTime(1000); 202 mInstance.recordCheckPointInternal("some.intent", "some.app", "reason1"); 203 204 assertEquals( 205 "Shutdown request from INTENT for reason reason1 " 206 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 207 + "Intent: some.intent\n" 208 + "Package: some.app\n\n", 209 dumpToString()); 210 } 211 212 @Test testIntentEntryWithoutReason()213 public void testIntentEntryWithoutReason() { 214 mTestInjector.setCurrentTime(1000); 215 mInstance.recordCheckPointInternal("some.intent", "some.app", null); 216 217 assertTrue(dumpToString().startsWith( 218 "Shutdown request from INTENT at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n")); 219 } 220 221 @Test testMultipleEntries()222 public void testMultipleEntries() { 223 mTestInjector.setCurrentTime(1000); 224 mInstance.recordCheckPointInternal(1, "reason1"); 225 mTestInjector.setCurrentTime(2000); 226 mInstance.recordCheckPointInternal(2, "reason2"); 227 mTestInjector.setCurrentTime(3000); 228 mInstance.recordCheckPointInternal("intent", "app", "reason3"); 229 230 assertEquals( 231 "Shutdown request from BINDER for reason reason1 " 232 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 233 + "com.android.server.power.ShutdownCheckPointsTest.testMultipleEntries\n" 234 + "From process ? (pid=1)\n\n" 235 + "Shutdown request from BINDER for reason reason2 " 236 + "at 1970-01-01 00:00:02.000 UTC (epoch=2000)\n" 237 + "com.android.server.power.ShutdownCheckPointsTest.testMultipleEntries\n" 238 + "From process ? (pid=2)\n\n" 239 + "Shutdown request from INTENT for reason reason3 " 240 + "at 1970-01-01 00:00:03.000 UTC (epoch=3000)\n" 241 + "Intent: intent\n" 242 + "Package: app\n\n", 243 dumpToString()); 244 } 245 246 @Test testTooManyEntriesDropsOlderOnes()247 public void testTooManyEntriesDropsOlderOnes() { 248 mTestInjector.setCheckPointsLimit(2); 249 ShutdownCheckPoints limitedInstance = new ShutdownCheckPoints(mTestInjector); 250 251 mTestInjector.setCurrentTime(1000); 252 limitedInstance.recordCheckPointInternal("intent.1", "app.1", "reason1"); 253 mTestInjector.setCurrentTime(2000); 254 limitedInstance.recordCheckPointInternal("intent.2", "app.2", "reason2"); 255 mTestInjector.setCurrentTime(3000); 256 limitedInstance.recordCheckPointInternal("intent.3", "app.3", "reason3"); 257 258 // Drops first intent. 259 assertEquals( 260 "Shutdown request from INTENT for reason reason2 " 261 + "at 1970-01-01 00:00:02.000 UTC (epoch=2000)\n" 262 + "Intent: intent.2\n" 263 + "Package: app.2\n\n" 264 + "Shutdown request from INTENT for reason reason3 " 265 + "at 1970-01-01 00:00:03.000 UTC (epoch=3000)\n" 266 + "Intent: intent.3\n" 267 + "Package: app.3\n\n", 268 dumpToString(limitedInstance)); 269 } 270 271 @Test testDumpToFile()272 public void testDumpToFile() throws Exception { 273 File tempDir = createTempDir(); 274 File baseFile = new File(tempDir, "checkpoints"); 275 276 mTestInjector.setCurrentTime(1000); 277 mInstance.recordCheckPointInternal("first.intent", "first.app", "reason1"); 278 dumpToFile(baseFile); 279 280 mTestInjector.setCurrentTime(2000); 281 mInstance.recordCheckPointInternal("second.intent", "second.app", "reason2"); 282 dumpToFile(baseFile); 283 284 File[] dumpFiles = tempDir.listFiles(); 285 Arrays.sort(dumpFiles); 286 287 assertEquals(2, dumpFiles.length); 288 assertEquals( 289 "Shutdown request from INTENT for reason reason1 " 290 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 291 + "Intent: first.intent\n" 292 + "Package: first.app\n\n", 293 readFileAsString(dumpFiles[0].getAbsolutePath())); 294 assertEquals( 295 "Shutdown request from INTENT for reason reason1 " 296 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 297 + "Intent: first.intent\n" 298 + "Package: first.app\n\n" 299 + "Shutdown request from INTENT for reason reason2 " 300 + "at 1970-01-01 00:00:02.000 UTC (epoch=2000)\n" 301 + "Intent: second.intent\n" 302 + "Package: second.app\n\n", 303 readFileAsString(dumpFiles[1].getAbsolutePath())); 304 } 305 306 @Test testTooManyFilesDropsOlderOnes()307 public void testTooManyFilesDropsOlderOnes() throws Exception { 308 mTestInjector.setDumpFilesLimit(1); 309 ShutdownCheckPoints instance = new ShutdownCheckPoints(mTestInjector); 310 File tempDir = createTempDir(); 311 File baseFile = new File(tempDir, "checkpoints"); 312 313 mTestInjector.setCurrentTime(1000); 314 instance.recordCheckPointInternal("first.intent", "first.app", "reason1"); 315 dumpToFile(instance, baseFile); 316 317 mTestInjector.setCurrentTime(2000); 318 instance.recordCheckPointInternal("second.intent", "second.app", "reason2"); 319 dumpToFile(instance, baseFile); 320 321 File[] dumpFiles = tempDir.listFiles(); 322 assertEquals(1, dumpFiles.length); 323 assertEquals( 324 "Shutdown request from INTENT for reason reason1 " 325 + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n" 326 + "Intent: first.intent\n" 327 + "Package: first.app\n\n" 328 + "Shutdown request from INTENT for reason reason2 " 329 + "at 1970-01-01 00:00:02.000 UTC (epoch=2000)\n" 330 + "Intent: second.intent\n" 331 + "Package: second.app\n\n", 332 readFileAsString(dumpFiles[0].getAbsolutePath())); 333 } 334 dumpToString()335 private String dumpToString() { 336 return dumpToString(mInstance); 337 } 338 dumpToString(ShutdownCheckPoints instance)339 private String dumpToString(ShutdownCheckPoints instance) { 340 StringWriter sw = new StringWriter(); 341 PrintWriter pw = new PrintWriter(sw); 342 instance.dumpInternal(pw); 343 return sw.toString(); 344 } 345 dumpToFile(File baseFile)346 private void dumpToFile(File baseFile) throws InterruptedException { 347 dumpToFile(mInstance, baseFile); 348 } 349 dumpToFile(ShutdownCheckPoints instance, File baseFile)350 private void dumpToFile(ShutdownCheckPoints instance, File baseFile) 351 throws InterruptedException { 352 Thread dumpThread = instance.newDumpThreadInternal(baseFile); 353 dumpThread.start(); 354 dumpThread.join(); 355 } 356 readFileAsString(String absolutePath)357 private String readFileAsString(String absolutePath) throws IOException { 358 return new String(Files.readAllBytes(Paths.get(absolutePath)), StandardCharsets.UTF_8); 359 } 360 createTempDir()361 private File createTempDir() throws IOException { 362 File tempDir = File.createTempFile("checkpoints", "out"); 363 tempDir.delete(); 364 tempDir.mkdir(); 365 return tempDir; 366 } 367 368 /** Fake system dependencies for testing. */ 369 private static final class TestInjector implements ShutdownCheckPoints.Injector { 370 private long mNow; 371 private int mCheckPointsLimit; 372 private int mDumpFilesLimit; 373 private IActivityManager mActivityManager; 374 TestInjector(IActivityManager activityManager)375 TestInjector(IActivityManager activityManager) { 376 mNow = 0; 377 mCheckPointsLimit = 100; 378 mDumpFilesLimit = 2; 379 mActivityManager = activityManager; 380 } 381 382 @Override currentTimeMillis()383 public long currentTimeMillis() { 384 return mNow; 385 } 386 387 @Override maxCheckPoints()388 public int maxCheckPoints() { 389 return mCheckPointsLimit; 390 } 391 392 @Override maxDumpFiles()393 public int maxDumpFiles() { 394 return mDumpFilesLimit; 395 } 396 397 @Override activityManager()398 public IActivityManager activityManager() { 399 return mActivityManager; 400 } 401 setCurrentTime(long time)402 void setCurrentTime(long time) { 403 mNow = time; 404 } 405 setCheckPointsLimit(int limit)406 void setCheckPointsLimit(int limit) { 407 mCheckPointsLimit = limit; 408 } 409 setDumpFilesLimit(int dumpFilesLimit)410 void setDumpFilesLimit(int dumpFilesLimit) { 411 mDumpFilesLimit = dumpFilesLimit; 412 } 413 } 414 } 415