2020-06-10 18:40:58 -07:00
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2020 The Android Open Source Project
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
package com.android.launcher3.testing;
|
|
|
|
|
|
2020-06-22 15:01:49 -07:00
|
|
|
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
2022-11-03 10:34:56 -07:00
|
|
|
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
2020-06-22 15:01:49 -07:00
|
|
|
|
2021-09-23 18:00:13 -07:00
|
|
|
import android.app.Activity;
|
|
|
|
|
import android.app.Application;
|
2020-06-10 18:40:58 -07:00
|
|
|
import android.content.Context;
|
2020-06-23 13:59:11 -07:00
|
|
|
import android.os.Binder;
|
2020-06-10 18:40:58 -07:00
|
|
|
import android.os.Bundle;
|
|
|
|
|
import android.system.Os;
|
|
|
|
|
|
|
|
|
|
import androidx.annotation.Keep;
|
2021-09-03 20:18:34 +00:00
|
|
|
import androidx.annotation.Nullable;
|
2020-06-10 18:40:58 -07:00
|
|
|
|
2022-02-10 11:10:21 -08:00
|
|
|
import com.android.launcher3.BubbleTextView;
|
2020-06-22 15:01:49 -07:00
|
|
|
import com.android.launcher3.LauncherAppState;
|
2023-05-17 12:44:03 -07:00
|
|
|
import com.android.launcher3.LauncherModel;
|
2022-02-10 11:10:21 -08:00
|
|
|
import com.android.launcher3.ShortcutAndWidgetContainer;
|
2022-07-26 13:54:31 -07:00
|
|
|
import com.android.launcher3.testing.shared.TestProtocol;
|
2020-06-22 15:01:49 -07:00
|
|
|
|
2020-06-19 15:00:00 -07:00
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Collection;
|
2021-09-23 18:00:13 -07:00
|
|
|
import java.util.Collections;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.WeakHashMap;
|
2020-06-10 18:40:58 -07:00
|
|
|
import java.util.concurrent.CountDownLatch;
|
|
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Class to handle requests from tests, including debug ones.
|
|
|
|
|
*/
|
|
|
|
|
public class DebugTestInformationHandler extends TestInformationHandler {
|
2020-06-19 15:00:00 -07:00
|
|
|
private static Collection<String> sEvents;
|
2021-09-23 18:00:13 -07:00
|
|
|
private static Application.ActivityLifecycleCallbacks sActivityLifecycleCallbacks;
|
|
|
|
|
private static final Map<Activity, Boolean> sActivities =
|
|
|
|
|
Collections.synchronizedMap(new WeakHashMap<>());
|
|
|
|
|
private static int sActivitiesCreatedCount = 0;
|
2020-06-10 18:40:58 -07:00
|
|
|
|
|
|
|
|
public DebugTestInformationHandler(Context context) {
|
|
|
|
|
init(context);
|
2021-09-23 18:00:13 -07:00
|
|
|
if (sActivityLifecycleCallbacks == null) {
|
|
|
|
|
sActivityLifecycleCallbacks = new Application.ActivityLifecycleCallbacks() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onActivityCreated(Activity activity, Bundle bundle) {
|
|
|
|
|
sActivities.put(activity, true);
|
|
|
|
|
++sActivitiesCreatedCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onActivityStarted(Activity activity) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onActivityResumed(Activity activity) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onActivityPaused(Activity activity) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onActivityStopped(Activity activity) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onActivityDestroyed(Activity activity) {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
((Application) context.getApplicationContext())
|
|
|
|
|
.registerActivityLifecycleCallbacks(sActivityLifecycleCallbacks);
|
|
|
|
|
}
|
2020-06-10 18:40:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void runGcAndFinalizersSync() {
|
|
|
|
|
Runtime.getRuntime().gc();
|
|
|
|
|
Runtime.getRuntime().runFinalization();
|
|
|
|
|
|
|
|
|
|
final CountDownLatch fence = new CountDownLatch(1);
|
|
|
|
|
createFinalizationObserver(fence);
|
|
|
|
|
try {
|
|
|
|
|
do {
|
|
|
|
|
Runtime.getRuntime().gc();
|
|
|
|
|
Runtime.getRuntime().runFinalization();
|
|
|
|
|
} while (!fence.await(100, TimeUnit.MILLISECONDS));
|
|
|
|
|
} catch (InterruptedException ex) {
|
|
|
|
|
throw new RuntimeException(ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create the observer in the scope of a method to minimize the chance that
|
|
|
|
|
// it remains live in a DEX/machine register at the point of the fence guard.
|
|
|
|
|
// This must be kept to avoid R8 inlining it.
|
|
|
|
|
@Keep
|
|
|
|
|
private static void createFinalizationObserver(CountDownLatch fence) {
|
|
|
|
|
new Object() {
|
|
|
|
|
@Override
|
|
|
|
|
protected void finalize() throws Throwable {
|
|
|
|
|
try {
|
|
|
|
|
fence.countDown();
|
|
|
|
|
} finally {
|
|
|
|
|
super.finalize();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2021-09-03 20:18:34 +00:00
|
|
|
public Bundle call(String method, String arg, @Nullable Bundle extras) {
|
2020-06-10 18:40:58 -07:00
|
|
|
final Bundle response = new Bundle();
|
|
|
|
|
switch (method) {
|
|
|
|
|
case TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS: {
|
|
|
|
|
return getLauncherUIProperty(Bundle::putInt,
|
|
|
|
|
l -> l.getAppsView().getAppsStore().getDeferUpdatesFlags());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case TestProtocol.REQUEST_ENABLE_DEBUG_TRACING:
|
|
|
|
|
TestProtocol.sDebugTracing = true;
|
|
|
|
|
return response;
|
|
|
|
|
|
|
|
|
|
case TestProtocol.REQUEST_DISABLE_DEBUG_TRACING:
|
|
|
|
|
TestProtocol.sDebugTracing = false;
|
|
|
|
|
return response;
|
|
|
|
|
|
|
|
|
|
case TestProtocol.REQUEST_PID: {
|
|
|
|
|
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, Os.getpid());
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-12 18:29:23 -07:00
|
|
|
case TestProtocol.REQUEST_FORCE_GC: {
|
2020-06-10 18:40:58 -07:00
|
|
|
runGcAndFinalizersSync();
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-19 15:00:00 -07:00
|
|
|
case TestProtocol.REQUEST_START_EVENT_LOGGING: {
|
|
|
|
|
sEvents = new ArrayList<>();
|
|
|
|
|
TestLogging.setEventConsumer(
|
|
|
|
|
(sequence, event) -> {
|
|
|
|
|
final Collection<String> events = sEvents;
|
|
|
|
|
if (events != null) {
|
|
|
|
|
synchronized (events) {
|
|
|
|
|
events.add(sequence + '/' + event);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case TestProtocol.REQUEST_STOP_EVENT_LOGGING: {
|
|
|
|
|
TestLogging.setEventConsumer(null);
|
|
|
|
|
sEvents = null;
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case TestProtocol.REQUEST_GET_TEST_EVENTS: {
|
2020-06-25 13:32:20 -07:00
|
|
|
if (sEvents == null) {
|
|
|
|
|
// sEvents can be null if Launcher died and restarted after
|
|
|
|
|
// REQUEST_START_EVENT_LOGGING.
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-19 15:00:00 -07:00
|
|
|
synchronized (sEvents) {
|
|
|
|
|
response.putStringArrayList(
|
|
|
|
|
TestProtocol.TEST_INFO_RESPONSE_FIELD, new ArrayList<>(sEvents));
|
|
|
|
|
}
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-27 11:31:32 -07:00
|
|
|
case TestProtocol.REQUEST_REINITIALIZE_DATA: {
|
|
|
|
|
final long identity = Binder.clearCallingIdentity();
|
|
|
|
|
try {
|
|
|
|
|
MODEL_EXECUTOR.execute(() -> {
|
|
|
|
|
LauncherModel model = LauncherAppState.getInstance(mContext).getModel();
|
|
|
|
|
model.getModelDbController().createEmptyDB();
|
|
|
|
|
MAIN_EXECUTOR.execute(model::forceReload);
|
|
|
|
|
});
|
|
|
|
|
return response;
|
|
|
|
|
} finally {
|
|
|
|
|
Binder.restoreCallingIdentity(identity);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-22 15:01:49 -07:00
|
|
|
case TestProtocol.REQUEST_CLEAR_DATA: {
|
2020-06-23 13:59:11 -07:00
|
|
|
final long identity = Binder.clearCallingIdentity();
|
|
|
|
|
try {
|
2023-05-17 12:44:03 -07:00
|
|
|
MODEL_EXECUTOR.execute(() -> {
|
|
|
|
|
LauncherModel model = LauncherAppState.getInstance(mContext).getModel();
|
|
|
|
|
model.getModelDbController().createEmptyDB();
|
2023-06-27 11:31:32 -07:00
|
|
|
model.getModelDbController().clearEmptyDbFlag();
|
2023-05-17 12:44:03 -07:00
|
|
|
MAIN_EXECUTOR.execute(model::forceReload);
|
|
|
|
|
});
|
2020-06-23 13:59:11 -07:00
|
|
|
return response;
|
|
|
|
|
} finally {
|
|
|
|
|
Binder.restoreCallingIdentity(identity);
|
|
|
|
|
}
|
2020-06-22 15:01:49 -07:00
|
|
|
}
|
|
|
|
|
|
2022-02-10 11:10:21 -08:00
|
|
|
case TestProtocol.REQUEST_HOTSEAT_ICON_NAMES: {
|
|
|
|
|
return getLauncherUIProperty(Bundle::putStringArrayList, l -> {
|
|
|
|
|
ShortcutAndWidgetContainer hotseatIconsContainer =
|
|
|
|
|
l.getHotseat().getShortcutsAndWidgets();
|
|
|
|
|
ArrayList<String> hotseatIconNames = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < hotseatIconsContainer.getChildCount(); i++) {
|
|
|
|
|
// Use unchecked cast to catch changes in hotseat layout
|
|
|
|
|
BubbleTextView icon = (BubbleTextView) hotseatIconsContainer.getChildAt(i);
|
|
|
|
|
hotseatIconNames.add((String) icon.getText());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return hotseatIconNames;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-23 18:00:13 -07:00
|
|
|
case TestProtocol.REQUEST_GET_ACTIVITIES_CREATED_COUNT: {
|
|
|
|
|
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, sActivitiesCreatedCount);
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case TestProtocol.REQUEST_GET_ACTIVITIES: {
|
|
|
|
|
response.putStringArray(TestProtocol.TEST_INFO_RESPONSE_FIELD,
|
|
|
|
|
sActivities.keySet().stream().map(
|
|
|
|
|
a -> a.getClass().getSimpleName() + " ("
|
|
|
|
|
+ (a.isDestroyed() ? "destroyed" : "current") + ")")
|
|
|
|
|
.toArray(String[]::new));
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-03 10:34:56 -07:00
|
|
|
case TestProtocol.REQUEST_MODEL_QUEUE_CLEARED:
|
|
|
|
|
return getFromExecutorSync(MODEL_EXECUTOR, Bundle::new);
|
|
|
|
|
|
2020-06-10 18:40:58 -07:00
|
|
|
default:
|
2021-09-03 20:18:34 +00:00
|
|
|
return super.call(method, arg, extras);
|
2020-06-10 18:40:58 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|