| /* |
| * Copyright (C) 2024 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 test; |
| |
| import static android.uprobestats.flags.Flags.FLAG_ENABLE_UPROBESTATS; |
| import static android.uprobestats.flags.Flags.FLAG_EXECUTABLE_METHOD_FILE_OFFSETS; |
| import static android.uprobestats.mainline.flags.Flags.FLAG_ENABLE_BITMAP_INSTRUMENTATION; |
| import static android.uprobestats.mainline.flags.Flags.FLAG_ENABLE_BITMAP_SNAPSHOT; |
| import static android.uprobestats.mainline.flags.Flags.FLAG_UPROBESTATS_MONITOR_DISRUPTIVE_APP_ACTIVITIES; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import static org.junit.Assume.assumeTrue; |
| |
| import static test.SmokeTestSetup.configureStatsDAndStartUprobeStats; |
| import static test.SmokeTestSetup.initializeStatsD; |
| import static test.SmokeTestSetup.initializeUprobeStats; |
| |
| import android.cts.statsdatom.lib.AtomTestUtils; |
| import android.cts.statsdatom.lib.DeviceUtils; |
| import android.cts.statsdatom.lib.ReportUtils; |
| import android.platform.test.annotations.RequiresFlagsEnabled; |
| import android.platform.test.flag.junit.CheckFlagsRule; |
| import android.platform.test.flag.junit.host.HostFlagsValueProvider; |
| |
| import com.android.compatibility.common.util.CpuFeatures; |
| import com.android.os.StatsLog; |
| import com.android.os.uprobestats.AndroidGraphicsBitmapAllocated; |
| import com.android.os.uprobestats.AndroidGraphicsBitmapAllocationSnapshot; |
| import com.android.os.uprobestats.BindServiceLockedWithBalFlagsReported; |
| import com.android.os.uprobestats.SetComponentEnabledSettingReported; |
| import com.android.os.uprobestats.UprobestatsExtensionAtoms; |
| |
| import com.android.tradefed.device.DeviceNotAvailableException; |
| import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; |
| import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; |
| import com.android.tradefed.util.RunUtil; |
| |
| import com.google.protobuf.ExtensionRegistry; |
| |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import java.util.List; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.TimeoutException; |
| import java.util.stream.Stream; |
| |
| @RunWith(DeviceJUnit4ClassRunner.class) |
| public class SmokeTestRustOnly extends BaseHostJUnit4Test { |
| private static final String TEST_MALWARE_SIGNAL_CONFIG = "disruptive_app.textproto"; |
| private static final String BITMAP_ALLOCATION_CONFIG = "bitmap.textproto"; |
| private static final String BITMAP_ALLOCATION_SNAPSHOT_CONFIG = "bitmap_snapshot.textproto"; |
| private static final String BITMAP_TESTAPP_PACKAGE_NAME = "com.android.uprobestats.bitmap"; |
| private ExtensionRegistry mRegistry; |
| |
| @Rule |
| public final CheckFlagsRule mCheckFlagsRule = |
| HostFlagsValueProvider.createCheckFlagsRule(this::getDevice); |
| |
| @Before |
| public void setUp() throws Exception { |
| mRegistry = initializeStatsD(getDevice()); |
| initializeUprobeStats(getDevice()); |
| } |
| |
| @Test |
| @RequiresFlagsEnabled({ |
| FLAG_ENABLE_UPROBESTATS, |
| FLAG_EXECUTABLE_METHOD_FILE_OFFSETS, |
| FLAG_UPROBESTATS_MONITOR_DISRUPTIVE_APP_ACTIVITIES, |
| }) |
| public void disruptiveAppActivity() throws Exception { |
| assumeTrue(CpuFeatures.isArm64(getDevice())); |
| |
| configureStatsDAndStartUprobeStats( |
| getClass(), |
| getDevice(), |
| TEST_MALWARE_SIGNAL_CONFIG, |
| UprobestatsExtensionAtoms.SET_COMPONENT_ENABLED_SETTING_REPORTED_FIELD_NUMBER, |
| UprobestatsExtensionAtoms.BIND_SERVICE_LOCKED_WITH_BAL_FLAGS_REPORTED_FIELD_NUMBER); |
| |
| // enable and disable a component (need one that will definitely exist, but not in |
| // android/com.android namespace) |
| getDevice() |
| .executeShellCommand( |
| "pm disable" + " com.android.uprobestats.disruptive/.TestActivity"); |
| getDevice() |
| .executeShellCommand( |
| "pm enable" + " com.android.uprobestats.disruptive/.TestActivity"); |
| |
| getDevice() |
| .executeShellCommand( |
| "am start -n" + " com.android.uprobestats.disruptive/.TestActivity"); |
| |
| // Allow UprobeStats/StatsD time to collect metric |
| RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG); |
| |
| // See if the atom made it |
| List<StatsLog.EventMetricData> data = |
| ReportUtils.getEventMetricDataList(getDevice(), mRegistry); |
| assertThat(data.size()).isEqualTo(2); |
| |
| SetComponentEnabledSettingReported reported = |
| data.get(0) |
| .getAtom() |
| .getExtension(UprobestatsExtensionAtoms.setComponentEnabledSettingReported); |
| assertThat(reported.getNewState()) |
| .isEqualTo(2); // PackageManager.COMPONENT_ENABLED_STATE_DISABLED; |
| assertThat(reported.getPackageName()).isEqualTo("com.android.uprobestats.disruptive"); |
| assertThat(reported.getClassName()) |
| .isEqualTo("com.android.uprobestats.disruptive.TestActivity"); |
| assertThat(reported.getCallingPackageName()).isEqualTo("shell"); |
| |
| BindServiceLockedWithBalFlagsReported balReported = |
| data.get(1) |
| .getAtom() |
| .getExtension( |
| UprobestatsExtensionAtoms.bindServiceLockedWithBalFlagsReported); |
| assertThat(balReported.getCallingPackageName()) |
| .isEqualTo("com.android.uprobestats.disruptive"); |
| assertThat(balReported.getFlags()) |
| .isEqualTo(1048576); // Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS |
| assertThat(balReported.getIntentPackageName()).isEqualTo(""); |
| } |
| |
| @Test |
| @RequiresFlagsEnabled({ |
| FLAG_ENABLE_UPROBESTATS, |
| FLAG_EXECUTABLE_METHOD_FILE_OFFSETS, |
| com.android.art.flags.Flags.FLAG_EXECUTABLE_METHOD_FILE_OFFSETS_V2, |
| FLAG_ENABLE_BITMAP_INSTRUMENTATION, |
| }) |
| public void bitmapAllocation() throws Exception { |
| assumeTrue(CpuFeatures.isArm64(getDevice())); |
| final int uid = DeviceUtils.getAppUid(getDevice(), BITMAP_TESTAPP_PACKAGE_NAME); |
| |
| configureStatsDAndStartUprobeStats( |
| getClass(), |
| getDevice(), |
| BITMAP_ALLOCATION_CONFIG, |
| UprobestatsExtensionAtoms.ANDROID_GRAPHICS_BITMAP_ALLOCATED_FIELD_NUMBER); |
| |
| try (AutoCloseable a = |
| DeviceUtils.withActivity( |
| getDevice(), |
| BITMAP_TESTAPP_PACKAGE_NAME, |
| "BitmapTestActivity", |
| "action", |
| "action.lmk")) { |
| |
| // Allow UprobeStats/StatsD time to collect metric |
| RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG); |
| |
| // See if the atom made it |
| List<StatsLog.EventMetricData> data = |
| ReportUtils.getEventMetricDataList(getDevice(), mRegistry); |
| assertThat(data.size()).isGreaterThan(0); |
| boolean anyMatch = |
| data.stream() |
| .map(StatsLog.EventMetricData::getAtom) |
| .filter( |
| atom -> |
| atom.hasExtension( |
| UprobestatsExtensionAtoms |
| .androidGraphicsBitmapAllocated)) |
| .map( |
| atom -> |
| atom.getExtension( |
| UprobestatsExtensionAtoms |
| .androidGraphicsBitmapAllocated)) |
| .anyMatch( |
| reported -> |
| reported.getWidth() == 100 |
| && reported.getHeight() == 100 |
| && reported.getUid() == uid); |
| assertThat(anyMatch).isTrue(); |
| } |
| } |
| |
| private void waitForUprobeStatsToExit(int timeoutSeconds) |
| throws TimeoutException, DeviceNotAvailableException { |
| long startTime = System.currentTimeMillis(); |
| while (System.currentTimeMillis() - startTime < TimeUnit.SECONDS.toMillis(timeoutSeconds)) { |
| String pid = getDevice().executeShellCommand("pidof uprobestats"); |
| if (pid.isEmpty() || !pid.trim().matches("\\d+")) { |
| return; |
| } |
| try { |
| Thread.sleep(1000); |
| } catch (InterruptedException e) { |
| Thread.currentThread().interrupt(); |
| throw new TimeoutException("Interrupted while waiting for uprobestats to exit"); |
| } |
| } |
| throw new TimeoutException("Timeout waiting for uprobestats to exit"); |
| } |
| |
| @Test |
| @RequiresFlagsEnabled({ |
| FLAG_ENABLE_UPROBESTATS, |
| FLAG_EXECUTABLE_METHOD_FILE_OFFSETS, |
| com.android.art.flags.Flags.FLAG_EXECUTABLE_METHOD_FILE_OFFSETS_V2, |
| FLAG_ENABLE_BITMAP_INSTRUMENTATION, |
| FLAG_ENABLE_BITMAP_SNAPSHOT, |
| }) |
| public void bitmapAllocationSnapshot() throws Exception { |
| assumeTrue(CpuFeatures.isArm64(getDevice())); |
| final int uid = DeviceUtils.getAppUid(getDevice(), BITMAP_TESTAPP_PACKAGE_NAME); |
| |
| configureStatsDAndStartUprobeStats( |
| getClass(), |
| getDevice(), |
| BITMAP_ALLOCATION_SNAPSHOT_CONFIG, |
| UprobestatsExtensionAtoms.ANDROID_GRAPHICS_BITMAP_ALLOCATION_SNAPSHOT_FIELD_NUMBER); |
| |
| try (AutoCloseable a = |
| DeviceUtils.withActivity( |
| getDevice(), |
| BITMAP_TESTAPP_PACKAGE_NAME, |
| "BitmapTestActivity", |
| "action", |
| "action.lmk")) { |
| |
| // Allow UprobeStats/StatsD time to collect metric |
| RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG); |
| |
| getDevice().executeShellCommand("dumpsys meminfo " + |
| "com.android.uprobestats.bitmap"); |
| |
| // Wait until the uprobestats process exits. |
| waitForUprobeStatsToExit(35); |
| |
| // See if the atom made it |
| List<StatsLog.EventMetricData> data = |
| ReportUtils.getEventMetricDataList(getDevice(), mRegistry); |
| assertThat(data.size()).isGreaterThan(0); |
| Stream<AndroidGraphicsBitmapAllocationSnapshot> randomSampleSnapshot = |
| data.stream() |
| .map(StatsLog.EventMetricData::getAtom) |
| .filter( |
| atom -> |
| atom.hasExtension( |
| UprobestatsExtensionAtoms |
| .androidGraphicsBitmapAllocationSnapshot)) |
| .map( |
| atom -> |
| atom.getExtension( |
| UprobestatsExtensionAtoms |
| .androidGraphicsBitmapAllocationSnapshot)) |
| .filter( |
| reported -> |
| reported.getSnapshotType() |
| == AndroidGraphicsBitmapAllocationSnapshot |
| .SnapshotType |
| .SNAPSHOT_TYPE_RANDOM_SAMPLE); |
| assertThat( |
| randomSampleSnapshot |
| .filter( |
| reported -> |
| reported.getWidth() == 48 |
| && reported.getHeight() == 48 |
| && reported.getUid() == uid) |
| .count()) |
| .isEqualTo(1); |
| Stream<AndroidGraphicsBitmapAllocationSnapshot> maxAllocationSizeSnapshot = |
| data.stream() |
| .map(StatsLog.EventMetricData::getAtom) |
| .filter( |
| atom -> |
| atom.hasExtension( |
| UprobestatsExtensionAtoms |
| .androidGraphicsBitmapAllocationSnapshot)) |
| .map( |
| atom -> |
| atom.getExtension( |
| UprobestatsExtensionAtoms |
| .androidGraphicsBitmapAllocationSnapshot)) |
| .filter( |
| reported -> |
| reported.getSnapshotType() |
| == AndroidGraphicsBitmapAllocationSnapshot |
| .SnapshotType |
| .SNAPSHOT_TYPE_MAX_ALLOCATION_SIZE); |
| assertThat( |
| maxAllocationSizeSnapshot |
| .filter( |
| reported -> |
| reported.getWidth() == 48 |
| && reported.getHeight() == 48 |
| && reported.getUid() == uid) |
| .count()) |
| .isEqualTo(2); |
| } |
| } |
| } |