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.launcher3.testing; 18 19 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; 20 21 import android.content.Context; 22 import android.os.Binder; 23 import android.os.Bundle; 24 import android.system.Os; 25 import android.view.View; 26 27 import androidx.annotation.Keep; 28 29 import com.android.launcher3.LauncherAppState; 30 import com.android.launcher3.LauncherSettings; 31 32 import java.util.ArrayList; 33 import java.util.Collection; 34 import java.util.LinkedList; 35 import java.util.concurrent.CountDownLatch; 36 import java.util.concurrent.TimeUnit; 37 38 /** 39 * Class to handle requests from tests, including debug ones. 40 */ 41 public class DebugTestInformationHandler extends TestInformationHandler { 42 private static LinkedList sLeaks; 43 private static Collection<String> sEvents; 44 DebugTestInformationHandler(Context context)45 public DebugTestInformationHandler(Context context) { 46 init(context); 47 } 48 runGcAndFinalizersSync()49 private static void runGcAndFinalizersSync() { 50 Runtime.getRuntime().gc(); 51 Runtime.getRuntime().runFinalization(); 52 53 final CountDownLatch fence = new CountDownLatch(1); 54 createFinalizationObserver(fence); 55 try { 56 do { 57 Runtime.getRuntime().gc(); 58 Runtime.getRuntime().runFinalization(); 59 } while (!fence.await(100, TimeUnit.MILLISECONDS)); 60 } catch (InterruptedException ex) { 61 throw new RuntimeException(ex); 62 } 63 } 64 65 // Create the observer in the scope of a method to minimize the chance that 66 // it remains live in a DEX/machine register at the point of the fence guard. 67 // This must be kept to avoid R8 inlining it. 68 @Keep createFinalizationObserver(CountDownLatch fence)69 private static void createFinalizationObserver(CountDownLatch fence) { 70 new Object() { 71 @Override 72 protected void finalize() throws Throwable { 73 try { 74 fence.countDown(); 75 } finally { 76 super.finalize(); 77 } 78 } 79 }; 80 } 81 82 @Override call(String method)83 public Bundle call(String method) { 84 final Bundle response = new Bundle(); 85 switch (method) { 86 case TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS: { 87 return getLauncherUIProperty(Bundle::putInt, 88 l -> l.getAppsView().getAppsStore().getDeferUpdatesFlags()); 89 } 90 91 case TestProtocol.REQUEST_ENABLE_DEBUG_TRACING: 92 TestProtocol.sDebugTracing = true; 93 return response; 94 95 case TestProtocol.REQUEST_DISABLE_DEBUG_TRACING: 96 TestProtocol.sDebugTracing = false; 97 return response; 98 99 case TestProtocol.REQUEST_PID: { 100 response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, Os.getpid()); 101 return response; 102 } 103 104 case TestProtocol.REQUEST_FORCE_GC: { 105 runGcAndFinalizersSync(); 106 return response; 107 } 108 109 case TestProtocol.REQUEST_VIEW_LEAK: { 110 if (sLeaks == null) sLeaks = new LinkedList(); 111 sLeaks.add(new View(mContext)); 112 sLeaks.add(new View(mContext)); 113 return response; 114 } 115 116 case TestProtocol.REQUEST_START_EVENT_LOGGING: { 117 sEvents = new ArrayList<>(); 118 TestLogging.setEventConsumer( 119 (sequence, event) -> { 120 final Collection<String> events = sEvents; 121 if (events != null) { 122 synchronized (events) { 123 events.add(sequence + '/' + event); 124 } 125 } 126 }); 127 return response; 128 } 129 130 case TestProtocol.REQUEST_STOP_EVENT_LOGGING: { 131 TestLogging.setEventConsumer(null); 132 sEvents = null; 133 return response; 134 } 135 136 case TestProtocol.REQUEST_GET_TEST_EVENTS: { 137 if (sEvents == null) { 138 // sEvents can be null if Launcher died and restarted after 139 // REQUEST_START_EVENT_LOGGING. 140 return response; 141 } 142 143 synchronized (sEvents) { 144 response.putStringArrayList( 145 TestProtocol.TEST_INFO_RESPONSE_FIELD, new ArrayList<>(sEvents)); 146 } 147 return response; 148 } 149 150 case TestProtocol.REQUEST_CLEAR_DATA: { 151 final long identity = Binder.clearCallingIdentity(); 152 try { 153 LauncherSettings.Settings.call(mContext.getContentResolver(), 154 LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); 155 MAIN_EXECUTOR.submit(() -> 156 LauncherAppState.getInstance(mContext).getModel().forceReload()); 157 return response; 158 } finally { 159 Binder.restoreCallingIdentity(identity); 160 } 161 } 162 163 default: 164 return super.call(method); 165 } 166 } 167 } 168