Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2853caae authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Remove functional test-case moved to cts" into main

parents 8d1c9d10 27b1b405
Loading
Loading
Loading
Loading
+0 −3
Original line number Original line Diff line number Diff line
@@ -29,8 +29,6 @@ android_test {
        "src/**/*.java",
        "src/**/*.java",
        "src/**/*.kt",
        "src/**/*.kt",


        "test-apps/JobTestApp/src/**/*.java",

        "test-apps/SuspendTestApp/src/**/*.java",
        "test-apps/SuspendTestApp/src/**/*.java",
    ],
    ],
    static_libs: [
    static_libs: [
@@ -124,7 +122,6 @@ android_test {
    },
    },


    data: [
    data: [
        ":JobTestApp",
        ":SimpleServiceTestApp1",
        ":SimpleServiceTestApp1",
        ":SimpleServiceTestApp2",
        ":SimpleServiceTestApp2",
        ":SimpleServiceTestApp3",
        ":SimpleServiceTestApp3",
+0 −1
Original line number Original line Diff line number Diff line
@@ -29,7 +29,6 @@
        <option name="cleanup-apks" value="true" />
        <option name="cleanup-apks" value="true" />
        <option name="install-arg" value="-t" />
        <option name="install-arg" value="-t" />
        <option name="test-file-name" value="FrameworksServicesTests.apk" />
        <option name="test-file-name" value="FrameworksServicesTests.apk" />
        <option name="test-file-name" value="JobTestApp.apk" />
        <option name="test-file-name" value="SuspendTestApp.apk" />
        <option name="test-file-name" value="SuspendTestApp.apk" />
        <option name="test-file-name" value="SimpleServiceTestApp1.apk" />
        <option name="test-file-name" value="SimpleServiceTestApp1.apk" />
        <option name="test-file-name" value="SimpleServiceTestApp2.apk" />
        <option name="test-file-name" value="SimpleServiceTestApp2.apk" />
+0 −233
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2017 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.server.job;

import static com.android.servicestests.apps.jobtestapp.TestJobService.ACTION_JOB_STARTED;
import static com.android.servicestests.apps.jobtestapp.TestJobService.ACTION_JOB_STOPPED;
import static com.android.servicestests.apps.jobtestapp.TestJobService.JOB_PARAMS_EXTRA_KEY;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.job.JobParameters;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IDeviceIdleController;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Log;

import androidx.test.InstrumentationRegistry;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.servicestests.apps.jobtestapp.TestJobActivity;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

/**
 * Tests that background restrictions on jobs work as expected.
 * This test requires test-apps/JobTestApp to be installed on the device.
 * To run this test from root of checkout:
 * <pre>
 *  mmm -j32 frameworks/base/services/tests/servicestests/
 *  adb install -r $OUT/data/app/JobTestApp/JobTestApp.apk
 *  adb install -r $OUT/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
 *  adb  shell am instrument -e class 'com.android.server.job.BackgroundRestrictionsTest' -w \
 *  com.android.frameworks.servicestests
 * </pre>
 */
@RunWith(AndroidJUnit4.class)
@LargeTest
public class BackgroundRestrictionsTest {
    private static final String TAG = BackgroundRestrictionsTest.class.getSimpleName();
    private static final String TEST_APP_PACKAGE = "com.android.servicestests.apps.jobtestapp";
    private static final String TEST_APP_ACTIVITY = TEST_APP_PACKAGE + ".TestJobActivity";
    private static final long POLL_INTERVAL = 500;
    private static final long DEFAULT_WAIT_TIMEOUT = 10_000;

    private Context mContext;
    private AppOpsManager mAppOpsManager;
    private IDeviceIdleController mDeviceIdleController;
    private IActivityManager mIActivityManager;
    private volatile int mTestJobId = -1;
    private int mTestPackageUid;
    /* accesses must be synchronized on itself */
    private final TestJobStatus mTestJobStatus = new TestJobStatus();
    private final BroadcastReceiver mJobStateChangeReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final JobParameters params = intent.getParcelableExtra(JOB_PARAMS_EXTRA_KEY);
            Log.d(TAG, "Received action " + intent.getAction());
            synchronized (mTestJobStatus) {
                switch (intent.getAction()) {
                    case ACTION_JOB_STARTED:
                        mTestJobStatus.running = true;
                        mTestJobStatus.jobId = params.getJobId();
                        mTestJobStatus.stopReason = JobParameters.STOP_REASON_UNDEFINED;
                        break;
                    case ACTION_JOB_STOPPED:
                        mTestJobStatus.running = false;
                        mTestJobStatus.jobId = params.getJobId();
                        mTestJobStatus.stopReason = params.getStopReason();
                        break;
                }
            }
        }
    };

    @Before
    public void setUp() throws Exception {
        mContext = InstrumentationRegistry.getTargetContext();
        mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
        mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
                ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
        mIActivityManager = ActivityManager.getService();
        mTestPackageUid = mContext.getPackageManager().getPackageUid(TEST_APP_PACKAGE, 0);
        mTestJobStatus.reset();
        final IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ACTION_JOB_STARTED);
        intentFilter.addAction(ACTION_JOB_STOPPED);
        mContext.registerReceiver(mJobStateChangeReceiver, intentFilter,
                Context.RECEIVER_EXPORTED_UNAUDITED);
        setAppOpsModeAllowed(true);
        setPowerExemption(false);
    }

    private void scheduleTestJob() {
        mTestJobId = (int) (SystemClock.uptimeMillis() / 1000);
        final Intent scheduleJobIntent = new Intent(TestJobActivity.ACTION_START_JOB);
        scheduleJobIntent.putExtra(TestJobActivity.EXTRA_JOB_ID_KEY, mTestJobId);
        scheduleJobIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        scheduleJobIntent.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY));
        mContext.startActivity(scheduleJobIntent);
    }

    private void scheduleAndAssertJobStarted() throws Exception {
        scheduleTestJob();
        Thread.sleep(TestJobActivity.JOB_MINIMUM_LATENCY);
        assertTrue("Job did not start after scheduling", awaitJobStart(DEFAULT_WAIT_TIMEOUT));
    }

    @FlakyTest
    @Test
    public void testPowerExemption() throws Exception {
        scheduleAndAssertJobStarted();
        setAppOpsModeAllowed(false);
        mIActivityManager.makePackageIdle(TEST_APP_PACKAGE, UserHandle.USER_CURRENT);
        assertTrue("Job did not stop after putting app under bg-restriction",
                awaitJobStop(DEFAULT_WAIT_TIMEOUT,
                        JobParameters.STOP_REASON_BACKGROUND_RESTRICTION));

        setPowerExemption(true);
        scheduleTestJob();
        Thread.sleep(TestJobActivity.JOB_MINIMUM_LATENCY);
        assertTrue("Job did not start when the app was in the power exemption list",
                awaitJobStart(DEFAULT_WAIT_TIMEOUT));

        setPowerExemption(false);
        assertTrue("Job did not stop after removing from the power exemption list",
                awaitJobStop(DEFAULT_WAIT_TIMEOUT,
                        JobParameters.STOP_REASON_BACKGROUND_RESTRICTION));

        scheduleTestJob();
        Thread.sleep(TestJobActivity.JOB_MINIMUM_LATENCY);
        assertFalse("Job started under bg-restrictions", awaitJobStart(DEFAULT_WAIT_TIMEOUT));
        setPowerExemption(true);
        assertTrue("Job did not start when the app was in the power exemption list",
                awaitJobStart(DEFAULT_WAIT_TIMEOUT));
    }

    @After
    public void tearDown() throws Exception {
        final Intent cancelJobsIntent = new Intent(TestJobActivity.ACTION_CANCEL_JOBS);
        cancelJobsIntent.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY));
        cancelJobsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        mContext.startActivity(cancelJobsIntent);
        mContext.unregisterReceiver(mJobStateChangeReceiver);
        Thread.sleep(500); // To avoid race with register in the next setUp
        setAppOpsModeAllowed(true);
        setPowerExemption(false);
    }

    private void setPowerExemption(boolean exempt) throws RemoteException {
        if (exempt) {
            mDeviceIdleController.addPowerSaveWhitelistApp(TEST_APP_PACKAGE);
        } else {
            mDeviceIdleController.removePowerSaveWhitelistApp(TEST_APP_PACKAGE);
        }
    }

    private void setAppOpsModeAllowed(boolean allow) {
        mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mTestPackageUid,
                TEST_APP_PACKAGE, allow ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
    }

    private boolean awaitJobStart(long timeout) throws InterruptedException {
        return waitUntilTrue(timeout, () -> {
            synchronized (mTestJobStatus) {
                return (mTestJobStatus.jobId == mTestJobId) && mTestJobStatus.running;
            }
        });
    }

    private boolean awaitJobStop(long timeout, @JobParameters.StopReason int expectedStopReason)
            throws InterruptedException {
        return waitUntilTrue(timeout, () -> {
            synchronized (mTestJobStatus) {
                return (mTestJobStatus.jobId == mTestJobId) && !mTestJobStatus.running
                        && (expectedStopReason == JobParameters.STOP_REASON_UNDEFINED
                        || mTestJobStatus.stopReason == expectedStopReason);
            }
        });
    }

    private boolean waitUntilTrue(long timeout, Condition condition) throws InterruptedException {
        final long deadLine = SystemClock.uptimeMillis() + timeout;
        do {
            Thread.sleep(POLL_INTERVAL);
        } while (!condition.isTrue() && SystemClock.uptimeMillis() < deadLine);
        return condition.isTrue();
    }

    private static final class TestJobStatus {
        int jobId;
        int stopReason;
        boolean running;

        private void reset() {
            running = false;
            stopReason = jobId = 0;
        }
    }

    private interface Condition {
        boolean isTrue();
    }
}
+0 −37
Original line number Original line Diff line number Diff line
// Copyright (C) 2017 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 {
    // See: http://go/android-license-faq
    // A large-scale-change added 'default_applicable_licenses' to import
    // all of the 'license_kinds' from "frameworks_base_license"
    // to get the below license kinds:
    //   SPDX-license-identifier-Apache-2.0
    default_applicable_licenses: ["frameworks_base_license"],
}

android_test_helper_app {
    name: "JobTestApp",

    sdk_version: "current",

    srcs: ["**/*.java"],

    dex_preopt: {
        enabled: false,
    },
    optimize: {
        enabled: false,
    },
}
+0 −27
Original line number Original line Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 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.
-->

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.android.servicestests.apps.jobtestapp">

    <application>
        <service android:name=".TestJobService"
                 android:permission="android.permission.BIND_JOB_SERVICE" />
        <activity android:name=".TestJobActivity"
                  android:exported="true" />
    </application>

</manifest>
 No newline at end of file
Loading