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

Commit 2f6bfcbd authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Calling opChanged on package suspend / unsuspend"

parents eee09619 3017fe4f
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -5825,16 +5825,16 @@ public abstract class PackageManager {
     * {@code android.permission.SUSPEND_APPS} can put any app on the device into a suspended state.
     *
     * <p>While in this state, the application's notifications will be hidden, any of its started
     * activities will be stopped and it will not be able to show toasts or dialogs or ring the
     * device. When the user tries to launch a suspended app, the system will, instead, show a
     * activities will be stopped and it will not be able to show toasts or dialogs or play audio.
     * When the user tries to launch a suspended app, the system will, instead, show a
     * dialog to the user informing them that they cannot use this app while it is suspended.
     *
     * <p>When an app is put into this state, the broadcast action
     * {@link Intent#ACTION_MY_PACKAGE_SUSPENDED} will be delivered to any of its broadcast
     * receivers that included this action in their intent-filters, <em>including manifest
     * receivers.</em> Similarly, a broadcast action {@link Intent#ACTION_MY_PACKAGE_UNSUSPENDED}
     * is delivered when a previously suspended app is taken out of this state.
     * </p>
     * is delivered when a previously suspended app is taken out of this state. Apps are expected to
     * use these to gracefully deal with transitions to and from this state.
     *
     * @return {@code true} if the calling package has been suspended, {@code false} otherwise.
     *
+24 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server;

import static android.app.AppOpsManager.OP_PLAY_AUDIO;
import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
import static android.app.AppOpsManager.UID_STATE_CACHED;
import static android.app.AppOpsManager.UID_STATE_FOREGROUND;
@@ -36,8 +37,11 @@ import android.app.AppOpsManager.HistoricalOpEntry;
import android.app.AppOpsManager.HistoricalPackageOps;
import android.app.AppOpsManagerInternal;
import android.app.AppOpsManagerInternal.CheckOpsDelegate;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
@@ -645,6 +649,26 @@ public class AppOpsService extends IAppOpsService.Stub {
            }
        }

        final IntentFilter packageSuspendFilter = new IntentFilter();
        packageSuspendFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
        packageSuspendFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
        mContext.registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                final int[] changedUids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
                final String[] changedPkgs = intent.getStringArrayExtra(
                        Intent.EXTRA_CHANGED_PACKAGE_LIST);
                final ArraySet<ModeCallback> callbacks = mOpModeWatchers.get(OP_PLAY_AUDIO);
                for (int i = 0; i < changedUids.length; i++) {
                    final int changedUid = changedUids[i];
                    final String changedPkg = changedPkgs[i];
                    // We trust packagemanager to insert matching uid and packageNames in the extras
                    mHandler.sendMessage(PooledLambda.obtainMessage(AppOpsService::notifyOpChanged,
                            AppOpsService.this, callbacks, OP_PLAY_AUDIO, changedUid, changedPkg));
                }
            }
        }, packageSuspendFilter);

        PackageManagerInternal packageManagerInternal = LocalServices.getService(
                PackageManagerInternal.class);
        packageManagerInternal.setExternalSourcesPolicy(
+34 −0
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@

package com.android.server.pm;

import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.OP_PLAY_AUDIO;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -35,12 +39,14 @@ import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.SuspendDialogInfo;
import android.content.res.Resources;
import android.media.AudioAttributes;
import android.os.BaseBundle;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
@@ -54,6 +60,8 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.servicestests.apps.suspendtestapp.SuspendTestActivity;
import com.android.servicestests.apps.suspendtestapp.SuspendTestReceiver;

@@ -550,6 +558,32 @@ public class SuspendPackagesTest {
        assertEquals(ACTION_REPORT_MY_PACKAGE_UNSUSPENDED, intentFromApp.getAction());
    }

    @Test
    public void testAudioOpBlockedOnSuspend() throws Exception {
        final IAppOpsService iAppOps = IAppOpsService.Stub.asInterface(
                ServiceManager.getService(Context.APP_OPS_SERVICE));
        final CountDownLatch latch = new CountDownLatch(1);
        final IAppOpsCallback watcher = new IAppOpsCallback.Stub() {
            @Override
            public void opChanged(int op, int uid, String packageName) {
                if (op == OP_PLAY_AUDIO && packageName.equals(TEST_APP_PACKAGE_NAME)) {
                    latch.countDown();
                }
            }
        };
        iAppOps.startWatchingMode(OP_PLAY_AUDIO, TEST_APP_PACKAGE_NAME, watcher);
        final int testPackageUid = mPackageManager.getPackageUid(TEST_APP_PACKAGE_NAME, 0);
        int audioOpMode = iAppOps.checkAudioOperation(OP_PLAY_AUDIO,
                AudioAttributes.USAGE_UNKNOWN, testPackageUid, TEST_APP_PACKAGE_NAME);
        assertEquals("Audio muted for unsuspended package", MODE_ALLOWED, audioOpMode);
        suspendTestPackage(null, null, null);
        assertTrue("AppOpsWatcher did not callback", latch.await(5, TimeUnit.SECONDS));
        audioOpMode = iAppOps.checkAudioOperation(OP_PLAY_AUDIO,
                AudioAttributes.USAGE_UNKNOWN, testPackageUid, TEST_APP_PACKAGE_NAME);
        assertEquals("Audio not muted for suspended package", MODE_IGNORED, audioOpMode);
        iAppOps.stopWatchingMode(watcher);
    }

    @After
    public void tearDown() throws IOException {
        mAppCommsReceiver.unregister();