Removing ext_test directory

Except for a few methods, most of the testing code was already in the main
codebase. Maintaining the additional source directory adds unnecessary overhead

Bug: 330920490
Test: Presubmit
Flag: None
Change-Id: I56c43ab7a4869b26858d622d0b8b14f286d50e90
This commit is contained in:
Sunny Goyal committed 2024-03-22 15:45:32 -07:00
1 parent dc6ec50558
commit 9d2ece156d
7 files changed
+199 -512

No files matched your search

+4 -23
View File
@@ -17,7 +17,7 @@ package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
min_launcher3_sdk_version = "26"
min_launcher3_sdk_version = "30"
// Common source files used to build launcher (java and kotlin)
// All sources are split so they can be reused in many other libraries/apps in other folders
@@ -69,22 +69,6 @@ filegroup {
],
}
filegroup {
name: "launcher-ext_tests",
srcs: [
"ext_tests/**/*.java",
"ext_tests/**/*.kt",
],
}
filegroup {
name: "launcher-quickstep-ext_tests",
srcs: [
"quickstep/ext_tests/**/*.java",
"quickstep/ext_tests/**/*.kt",
],
}
// Proguard files for Launcher3
filegroup {
name: "launcher-proguard-rules",
@@ -186,7 +170,7 @@ android_library {
sdk_version: "current",
min_sdk_version: min_launcher3_sdk_version,
lint: {
baseline_filename: "lint-baseline2.xml",
baseline_filename: "lint-baseline.xml",
},
}
@@ -212,7 +196,7 @@ android_library {
min_sdk_version: min_launcher3_sdk_version,
manifest: "AndroidManifest-common.xml",
lint: {
baseline_filename: "lint-baseline2.xml",
baseline_filename: "lint-baseline.xml",
},
}
@@ -229,11 +213,8 @@ android_app {
":launcher-src",
":launcher-src_shortcuts_overrides",
":launcher-src_ui_overrides",
":launcher-ext_tests",
],
resource_dirs: [
"ext_tests/res",
],
optimize: {
proguard_flags_files: ["proguard.flags"],
// Proguard is disable for testing. Derivarive prjects to keep proguard enabled
-5
View File
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="test_information_handler_class" translatable="false">com.android.launcher3.testing.DebugTestInformationHandler</string>
</resources>
@@ -1,258 +0,0 @@
/*
* 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;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.os.Binder;
import android.os.Bundle;
import android.system.Os;
import androidx.annotation.Keep;
import androidx.annotation.Nullable;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.icons.ClockDrawableWrapper;
import com.android.launcher3.testing.shared.TestProtocol;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Class to handle requests from tests, including debug ones.
*/
public class DebugTestInformationHandler extends TestInformationHandler {
private static Collection<String> sEvents;
private static Application.ActivityLifecycleCallbacks sActivityLifecycleCallbacks;
private static final Map<Activity, Boolean> sActivities =
Collections.synchronizedMap(new WeakHashMap<>());
private static int sActivitiesCreatedCount = 0;
public DebugTestInformationHandler(Context context) {
init(context);
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);
}
}
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
public Bundle call(String method, String arg, @Nullable Bundle extras) {
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;
ClockDrawableWrapper.sRunningInTest = true;
return response;
case TestProtocol.REQUEST_DISABLE_DEBUG_TRACING:
TestProtocol.sDebugTracing = false;
ClockDrawableWrapper.sRunningInTest = false;
return response;
case TestProtocol.REQUEST_PID: {
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, Os.getpid());
return response;
}
case TestProtocol.REQUEST_FORCE_GC: {
runGcAndFinalizersSync();
return response;
}
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: {
if (sEvents == null) {
// sEvents can be null if Launcher died and restarted after
// REQUEST_START_EVENT_LOGGING.
return response;
}
synchronized (sEvents) {
response.putStringArrayList(
TestProtocol.TEST_INFO_RESPONSE_FIELD, new ArrayList<>(sEvents));
}
return response;
}
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);
}
}
case TestProtocol.REQUEST_CLEAR_DATA: {
final long identity = Binder.clearCallingIdentity();
try {
MODEL_EXECUTOR.execute(() -> {
LauncherModel model = LauncherAppState.getInstance(mContext).getModel();
model.getModelDbController().createEmptyDB();
model.getModelDbController().clearEmptyDbFlag();
MAIN_EXECUTOR.execute(model::forceReload);
});
return response;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
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;
});
}
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;
}
case TestProtocol.REQUEST_MODEL_QUEUE_CLEARED:
return getFromExecutorSync(MODEL_EXECUTOR, Bundle::new);
default:
return super.call(method, arg, extras);
}
}
}
+6 -138
View File
@@ -3,13 +3,13 @@
<issue
id="NewApi"
message="Call requires API level 29 (current min is 26): `android.content.res.Resources#getFloat`"
errorLine1=" return mContext.getResources().getFloat(resId);"
errorLine2=" ~~~~~~~~">
message="`@android:dimen/system_app_widget_background_radius` requires API level 31 (current min is 26)"
errorLine1=' &lt;corners android:radius="@android:dimen/system_app_widget_background_radius" /&gt;'
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/util/DynamicResource.java"
line="73"
column="40"/>
file="packages/apps/Launcher3/res/drawable/widget_resize_frame.xml"
line="20"
column="14"/>
</issue>
<issue
@@ -34,136 +34,4 @@
column="18"/>
</issue>
<issue
id="NewApi"
message="Call requires API level 28 (current min is 26): `android.app.Person#getKey`"
errorLine1=" return people.stream().filter(person -&gt; person.getKey() != null)"
errorLine2=" ~~~~~~">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/notification/NotificationKeyData.java"
line="72"
column="56"/>
</issue>
<issue
id="NewApi"
message="Method reference requires API level 28 (current min is 26): `Person::getKey`"
errorLine1=" .map(Person::getKey).sorted().toArray(String[]::new);"
errorLine2=" ~~~~~~~~~~~~~~">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/notification/NotificationKeyData.java"
line="73"
column="22"/>
</issue>
<issue
id="NewApi"
message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_LEFT`"
errorLine1=" AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
line="1814"
column="17"/>
</issue>
<issue
id="NewApi"
message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_RIGHT`"
errorLine1=" : AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT);"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
line="1815"
column="19"/>
</issue>
<issue
id="NewApi"
message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_RIGHT`"
errorLine1=" AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
line="1823"
column="17"/>
</issue>
<issue
id="NewApi"
message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_LEFT`"
errorLine1=" : AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT);"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
line="1824"
column="19"/>
</issue>
<issue
id="NewApi"
message="Call requires API level 30 (current min is 26): `android.graphics.Outline#setPath`"
errorLine1=" outline.setPath(mPath);"
errorLine2=" ~~~~~~~">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/popup/RoundedArrowDrawable.java"
line="114"
column="17"/>
</issue>
<issue
id="NewApi"
message="Field requires API level 28 (current min is 26): `android.appwidget.AppWidgetProviderInfo#widgetFeatures`"
errorLine1=" int featureFlags = mProviderInfo.widgetFeatures;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/widget/WidgetAddFlowHandler.java"
line="93"
column="28"/>
</issue>
<issue
id="NewApi"
message="Call requires API level 30 (current min is 26): `android.view.View#getWindowInsetsController`"
errorLine1=" WindowInsetsController insetsController = getWindowInsetsController();"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
line="820"
column="51"/>
</issue>
<issue
id="NewApi"
message="Call requires API level 30 (current min is 26): `android.view.WindowInsets.Type#ime`"
errorLine1=" insetsController.hide(WindowInsets.Type.ime());"
errorLine2=" ~~~">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
line="822"
column="53"/>
</issue>
<issue
id="NewApi"
message="Call requires API level 30 (current min is 26): `android.view.WindowInsetsController#hide`"
errorLine1=" insetsController.hide(WindowInsets.Type.ime());"
errorLine2=" ~~~~">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
line="822"
column="30"/>
</issue>
<issue
id="NewApi"
message="Method reference requires API level 28 (current min is 26): `Person::getKey`"
errorLine1=" : Arrays.stream(persons).map(Person::getKey).sorted().toArray(String[]::new);"
errorLine2=" ~~~~~~~~~~~~~~">
<location
file="packages/apps/Launcher3/src/com/android/launcher3/model/data/WorkspaceItemInfo.java"
line="194"
column="42"/>
</issue>
</issues>
-37
View File
@@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
<issue
id="NewApi"
message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 26)"
errorLine1=' android:topLeftRadius="?android:attr/dialogCornerRadius"'
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="packages/apps/Launcher3/res/drawable/add_item_dialog_background.xml"
line="6"
column="9"/>
</issue>
<issue
id="NewApi"
message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 26)"
errorLine1=' android:topRightRadius="?android:attr/dialogCornerRadius" /&gt;'
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="packages/apps/Launcher3/res/drawable/add_item_dialog_background.xml"
line="7"
column="9"/>
</issue>
<issue
id="NewApi"
message="`@android:dimen/system_app_widget_background_radius` requires API level 31 (current min is 26)"
errorLine1=' &lt;corners android:radius="@android:dimen/system_app_widget_background_radius" /&gt;'
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="packages/apps/Launcher3/res/drawable/widget_resize_frame.xml"
line="20"
column="14"/>
</issue>
</issues>
@@ -1,51 +0,0 @@
/*
* Copyright (C) 2022 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.quickstep;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.Nullable;
import com.android.launcher3.testing.DebugTestInformationHandler;
import com.android.launcher3.testing.shared.TestProtocol;
/**
* Class to handle requests from tests, including debug ones, to Quickstep Launcher builds.
*/
public abstract class DebugQuickstepTestInformationHandler extends QuickstepTestInformationHandler {
private final DebugTestInformationHandler mDebugTestInformationHandler;
public DebugQuickstepTestInformationHandler(Context context) {
super(context);
mDebugTestInformationHandler = new DebugTestInformationHandler(context);
}
@Override
public Bundle call(String method, String arg, @Nullable Bundle extras) {
Bundle response = new Bundle();
if (TestProtocol.REQUEST_RECREATE_TASKBAR.equals(method)) {
// Allow null-pointer to catch illegal states.
runOnTISBinder(tisBinder -> tisBinder.getTaskbarManager().recreateTaskbar());
return response;
}
response = super.call(method, arg, extras);
if (response != null) return response;
return mDebugTestInformationHandler.call(method, arg, extras);
}
}
@@ -23,39 +23,56 @@ import static com.android.launcher3.config.FeatureFlags.enableAppPairs;
import static com.android.launcher3.config.FeatureFlags.enableSplitContextually;
import static com.android.launcher3.testing.shared.TestProtocol.TEST_INFO_RESPONSE_FIELD;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
import android.system.Os;
import android.view.WindowInsets;
import androidx.annotation.Keep;
import androidx.annotation.Nullable;
import androidx.core.view.WindowInsetsCompat;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Hotseat;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Workspace;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.icons.ClockDrawableWrapper;
import com.android.launcher3.testing.shared.HotseatCellCenterRequest;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.testing.shared.WorkspaceCellCenterRequest;
import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.launcher3.widget.picker.WidgetsFullSheet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -69,6 +86,12 @@ public class TestInformationHandler implements ResourceBasedOverride {
context, R.string.test_information_handler_class);
}
private static Collection<String> sEvents;
private static Application.ActivityLifecycleCallbacks sActivityLifecycleCallbacks;
private static final Set<Activity> sActivities =
Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>()));
private static int sActivitiesCreatedCount = 0;
protected Context mContext;
protected DeviceProfile mDeviceProfile;
protected LauncherAppState mLauncherAppState;
@@ -78,6 +101,17 @@ public class TestInformationHandler implements ResourceBasedOverride {
mDeviceProfile = InvariantDeviceProfile.INSTANCE.
get(context).getDeviceProfile(context);
mLauncherAppState = LauncherAppState.getInstanceNoCreate();
if (sActivityLifecycleCallbacks == null) {
sActivityLifecycleCallbacks = new ActivityLifecycleCallbacksAdapter() {
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
sActivities.add(activity);
++sActivitiesCreatedCount;
}
};
((Application) context.getApplicationContext())
.registerActivityLifecycleCallbacks(sActivityLifecycleCallbacks);
}
}
/**
@@ -309,6 +343,127 @@ public class TestInformationHandler implements ResourceBasedOverride {
return response;
}
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;
ClockDrawableWrapper.sRunningInTest = true;
return response;
case TestProtocol.REQUEST_DISABLE_DEBUG_TRACING:
TestProtocol.sDebugTracing = false;
ClockDrawableWrapper.sRunningInTest = false;
return response;
case TestProtocol.REQUEST_PID: {
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, Os.getpid());
return response;
}
case TestProtocol.REQUEST_FORCE_GC: {
runGcAndFinalizersSync();
return response;
}
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: {
if (sEvents == null) {
// sEvents can be null if Launcher died and restarted after
// REQUEST_START_EVENT_LOGGING.
return response;
}
synchronized (sEvents) {
response.putStringArrayList(
TestProtocol.TEST_INFO_RESPONSE_FIELD, new ArrayList<>(sEvents));
}
return response;
}
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);
}
}
case TestProtocol.REQUEST_CLEAR_DATA: {
final long identity = Binder.clearCallingIdentity();
try {
MODEL_EXECUTOR.execute(() -> {
LauncherModel model = LauncherAppState.getInstance(mContext).getModel();
model.getModelDbController().createEmptyDB();
model.getModelDbController().clearEmptyDbFlag();
MAIN_EXECUTOR.execute(model::forceReload);
});
return response;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
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;
});
}
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.stream().map(
a -> a.getClass().getSimpleName() + " ("
+ (a.isDestroyed() ? "destroyed" : "current") + ")")
.toArray(String[]::new));
return response;
}
case TestProtocol.REQUEST_MODEL_QUEUE_CLEARED:
return getFromExecutorSync(MODEL_EXECUTOR, Bundle::new);
default:
return null;
}
@@ -387,4 +542,38 @@ public class TestInformationHandler implements ResourceBasedOverride {
*/
void set(Bundle b, String key, T value);
}
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();
}
}
};
}
}