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

Commit f3ff3b5f authored by George Chan's avatar George Chan Committed by Android (Google) Code Review
Browse files

Merge "Added new permission guard to BIC Service in preparation of adding SystemAPI." into main

parents 29b4ce10 e83659c9
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
package: "android.app"

flag {
     namespace: "background_install_control"
     name: "bic_client"
     description: "System API for background install control."
     is_fixed_read_only: true
     bug: "287507984"
}
+107 −71
Original line number Diff line number Diff line
@@ -16,7 +16,12 @@

package com.android.server.pm;

import static android.Manifest.permission.GET_BACKGROUND_INSTALLED_PACKAGES;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;

import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.app.Flags;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.content.Context;
@@ -27,6 +32,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.os.Binder;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
@@ -69,26 +75,29 @@ public class BackgroundInstallControlService extends SystemService {
    private static final String DISK_FILE_NAME = "states";
    private static final String DISK_DIR_NAME = "bic";

    private static final int MAX_FOREGROUND_TIME_FRAMES_SIZE = 10;
    private static final String ENFORCE_PERMISSION_ERROR_MSG =
            "User is not permitted to call service: ";

    private static final int MAX_FOREGROUND_TIME_FRAMES_SIZE = 10;
    private static final int MSG_USAGE_EVENT_RECEIVED = 0;
    private static final int MSG_PACKAGE_ADDED = 1;
    private static final int MSG_PACKAGE_REMOVED = 2;

    private final BinderService mBinderService;
    private final PackageManager mPackageManager;
    // TODO migrate all internal PackageManager calls to PackageManagerInternal where possible.
    // b/310983905
    private final PackageManagerInternal mPackageManagerInternal;
    private final PermissionManagerServiceInternal mPermissionManager;
    private final Handler mHandler;
    private final File mDiskFile;

    private final Context mContext;

    private SparseSetArray<String> mBackgroundInstalledPackages = null;

    // User ID -> package name -> set of foreground time frame
    private final SparseArrayMap<String,
            TreeSet<ForegroundTimeFrame>> mInstallerForegroundTimeFrames =
            new SparseArrayMap<>();
    private final SparseArrayMap<String, TreeSet<ForegroundTimeFrame>>
            mInstallerForegroundTimeFrames = new SparseArrayMap<>();

    public BackgroundInstallControlService(@NonNull Context context) {
        this(new InjectorImpl(context));
@@ -102,15 +111,13 @@ public class BackgroundInstallControlService extends SystemService {
        mPermissionManager = injector.getPermissionManager();
        mHandler = new EventHandler(injector.getLooper(), this);
        mDiskFile = injector.getDiskFile();
        mContext = injector.getContext();
        UsageStatsManagerInternal usageStatsManagerInternal =
                injector.getUsageStatsManagerInternal();
        usageStatsManagerInternal.registerListener(
                (userId, event) ->
                        mHandler.obtainMessage(MSG_USAGE_EVENT_RECEIVED,
                                userId,
                                0,
                                event).sendToTarget()
        );
                        mHandler.obtainMessage(MSG_USAGE_EVENT_RECEIVED, userId, 0, event)
                                .sendToTarget());
        mBinderService = new BinderService(this);
    }

@@ -124,12 +131,17 @@ public class BackgroundInstallControlService extends SystemService {
        @Override
        public ParceledListSlice<PackageInfo> getBackgroundInstalledPackages(
                @PackageManager.PackageInfoFlagsBits long flags, int userId) {
            if (Flags.bicClient()) {
                mService.enforceCallerPermissions();
            }
            if (!Build.IS_DEBUGGABLE) {
                return mService.getBackgroundInstalledPackages(flags, userId);
            }
            // The debug.transparency.bg-install-apps (only works for debuggable builds)
            // is used to set mock list of background installed apps for testing.
            // The list of apps' names is delimited by ",".
            // TODO: Remove after migrating test to new background install method using
            // {@link BackgroundInstallControlCallbackHelperTest}.installPackage b/310983905
            String propertyString = SystemProperties.get("debug.transparency.bg-install-apps");
            if (TextUtils.isEmpty(propertyString)) {
                return mService.getBackgroundInstalledPackages(flags, userId);
@@ -137,16 +149,24 @@ public class BackgroundInstallControlService extends SystemService {
                return mService.getMockBackgroundInstalledPackages(propertyString);
            }
        }

    }

    @RequiresPermission(GET_BACKGROUND_INSTALLED_PACKAGES)
    void enforceCallerPermissions() throws SecurityException {
        mContext.enforceCallingOrSelfPermission(GET_BACKGROUND_INSTALLED_PACKAGES,
                ENFORCE_PERMISSION_ERROR_MSG + GET_BACKGROUND_INSTALLED_PACKAGES);
    }

    @VisibleForTesting
    ParceledListSlice<PackageInfo> getBackgroundInstalledPackages(
            @PackageManager.PackageInfoFlagsBits long flags, int userId) {
        final long token = Binder.clearCallingIdentity();
        try {
            List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
                    PackageManager.PackageInfoFlags.of(flags), userId);

            initBackgroundInstalledPackages();

            ListIterator<PackageInfo> iter = packages.listIterator();
            while (iter.hasNext()) {
                String packageName = iter.next().packageName;
@@ -156,6 +176,9 @@ public class BackgroundInstallControlService extends SystemService {
            }

            return new ParceledListSlice<>(packages);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    /**
@@ -168,8 +191,9 @@ public class BackgroundInstallControlService extends SystemService {
        List<PackageInfo> mockPackages = new ArrayList<>();
        for (String name : mockPackageNames) {
            try {
                PackageInfo packageInfo = mPackageManager.getPackageInfo(name,
                        PackageManager.PackageInfoFlags.of(PackageManager.MATCH_ALL));
                PackageInfo packageInfo =
                        mPackageManager.getPackageInfo(
                                name, PackageManager.PackageInfoFlags.of(PackageManager.MATCH_ALL));
                mockPackages.add(packageInfo);
            } catch (PackageManager.NameNotFoundException e) {
                Slog.w(TAG, "Package's PackageInfo not found " + name);
@@ -190,18 +214,16 @@ public class BackgroundInstallControlService extends SystemService {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_USAGE_EVENT_RECEIVED: {
                    mService.handleUsageEvent((UsageEvents.Event) msg.obj, msg.arg1 /* userId */);
                case MSG_USAGE_EVENT_RECEIVED:
                    mService.handleUsageEvent(
                            (UsageEvents.Event) msg.obj, msg.arg1 /* userId */);
                    break;
                }
                case MSG_PACKAGE_ADDED: {
                case MSG_PACKAGE_ADDED:
                    mService.handlePackageAdd((String) msg.obj, msg.arg1 /* userId */);
                    break;
                }
                case MSG_PACKAGE_REMOVED: {
                case MSG_PACKAGE_REMOVED:
                    mService.handlePackageRemove((String) msg.obj, msg.arg1 /* userId */);
                    break;
                }
                default:
                    Slog.w(TAG, "Unknown message: " + msg.what);
            }
@@ -211,8 +233,9 @@ public class BackgroundInstallControlService extends SystemService {
    void handlePackageAdd(String packageName, int userId) {
        ApplicationInfo appInfo = null;
        try {
            appInfo = mPackageManager.getApplicationInfoAsUser(packageName,
                    PackageManager.ApplicationInfoFlags.of(0), userId);
            appInfo =
                    mPackageManager.getApplicationInfoAsUser(
                            packageName, PackageManager.ApplicationInfoFlags.of(0), userId);
        } catch (PackageManager.NameNotFoundException e) {
            Slog.w(TAG, "Package's appInfo not found " + packageName);
            return;
@@ -231,15 +254,18 @@ public class BackgroundInstallControlService extends SystemService {

        // the installers without INSTALL_PACKAGES perm can't perform
        // the installation in background. So we can just filter out them.
        if (mPermissionManager.checkPermission(installerPackageName,
                android.Manifest.permission.INSTALL_PACKAGES, Context.DEVICE_ID_DEFAULT,
                userId) != PackageManager.PERMISSION_GRANTED) {
        if (mPermissionManager.checkPermission(
                installerPackageName,
                android.Manifest.permission.INSTALL_PACKAGES,
                Context.DEVICE_ID_DEFAULT,
                userId)
                != PERMISSION_GRANTED) {
            return;
        }

        // convert up-time to current time.
        final long installTimestamp = System.currentTimeMillis()
                - (SystemClock.uptimeMillis() - appInfo.createTimestamp);
        final long installTimestamp =
                System.currentTimeMillis() - (SystemClock.uptimeMillis() - appInfo.createTimestamp);

        if (installedByAdb(initiatingPackageName)
                || wasForegroundInstallation(installerPackageName, userId, installTimestamp)) {
@@ -257,8 +283,8 @@ public class BackgroundInstallControlService extends SystemService {
        return PackageManagerServiceUtils.isInstalledByAdb(initiatingPackageName);
    }

    private boolean wasForegroundInstallation(String installerPackageName,
            int userId, long installTimestamp) {
    private boolean wasForegroundInstallation(
            String installerPackageName, int userId, long installTimestamp) {
        TreeSet<BackgroundInstallControlService.ForegroundTimeFrame> foregroundTimeFrames =
                mInstallerForegroundTimeFrames.get(userId, installerPackageName);

@@ -347,12 +373,12 @@ public class BackgroundInstallControlService extends SystemService {
            for (int i = 0; i < mBackgroundInstalledPackages.size(); i++) {
                int userId = mBackgroundInstalledPackages.keyAt(i);
                for (String packageName : mBackgroundInstalledPackages.get(userId)) {
                    long token = protoOutputStream.start(
                    long token =
                            protoOutputStream.start(
                                    BackgroundInstalledPackagesProto.BG_INSTALLED_PKG);
                    protoOutputStream.write(
                            BackgroundInstalledPackageProto.PACKAGE_NAME, packageName);
                    protoOutputStream.write(
                            BackgroundInstalledPackageProto.USER_ID, userId + 1);
                    protoOutputStream.write(BackgroundInstalledPackageProto.USER_ID, userId + 1);
                    protoOutputStream.end(token);
                }
            }
@@ -385,22 +411,27 @@ public class BackgroundInstallControlService extends SystemService {
                        != (int) BackgroundInstalledPackagesProto.BG_INSTALLED_PKG) {
                    continue;
                }
                long token = protoInputStream.start(
                        BackgroundInstalledPackagesProto.BG_INSTALLED_PKG);
                long token =
                        protoInputStream.start(BackgroundInstalledPackagesProto.BG_INSTALLED_PKG);
                String packageName = null;
                int userId = UserHandle.USER_NULL;
                while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
                    switch (protoInputStream.getFieldNumber()) {
                        case (int) BackgroundInstalledPackageProto.PACKAGE_NAME:
                            packageName = protoInputStream.readString(
                            packageName =
                                    protoInputStream.readString(
                                            BackgroundInstalledPackageProto.PACKAGE_NAME);
                            break;
                        case (int) BackgroundInstalledPackageProto.USER_ID:
                            userId = protoInputStream.readInt(
                                    BackgroundInstalledPackageProto.USER_ID) - 1;
                            userId =
                                    protoInputStream.readInt(
                                            BackgroundInstalledPackageProto.USER_ID)
                                            - 1;
                            break;
                        default:
                            Slog.w(TAG, "Undefined field in proto: "
                            Slog.w(
                                    TAG,
                                    "Undefined field in proto: "
                                            + protoInputStream.getFieldNumber());
                    }
                }
@@ -430,9 +461,12 @@ public class BackgroundInstallControlService extends SystemService {
        if (mInstallerForegroundTimeFrames.contains(userId, pkgName)) {
            return true;
        }
        return mPermissionManager.checkPermission(pkgName,
                android.Manifest.permission.INSTALL_PACKAGES, Context.DEVICE_ID_DEFAULT,
                userId) == PackageManager.PERMISSION_GRANTED;
        return mPermissionManager.checkPermission(
                pkgName,
                android.Manifest.permission.INSTALL_PACKAGES,
                Context.DEVICE_ID_DEFAULT,
                userId)
                == PERMISSION_GRANTED;
    }

    @Override
@@ -446,19 +480,20 @@ public class BackgroundInstallControlService extends SystemService {
            publishBinderService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE, mBinderService);
        }

        mPackageManagerInternal.getPackageList(new PackageManagerInternal.PackageListObserver() {
        mPackageManagerInternal.getPackageList(
                new PackageManagerInternal.PackageListObserver() {
                    @Override
                    public void onPackageAdded(String packageName, int uid) {
                        final int userId = UserHandle.getUserId(uid);
                mHandler.obtainMessage(MSG_PACKAGE_ADDED,
                        userId, 0, packageName).sendToTarget();
                        mHandler.obtainMessage(MSG_PACKAGE_ADDED, userId, 0, packageName)
                                .sendToTarget();
                    }

                    @Override
                    public void onPackageRemoved(String packageName, int uid) {
                        final int userId = UserHandle.getUserId(uid);
                mHandler.obtainMessage(MSG_PACKAGE_REMOVED,
                        userId, 0, packageName).sendToTarget();
                        mHandler.obtainMessage(MSG_PACKAGE_REMOVED, userId, 0, packageName)
                                .sendToTarget();
                    }
                });
    }
@@ -516,7 +551,7 @@ public class BackgroundInstallControlService extends SystemService {
    }

    /**
     * Dependency injector for {@link #BackgroundInstallControlService)}.
     * Dependency injector for {@link BackgroundInstallControlService}.
     */
    interface Injector {
        Context getContext();
@@ -532,6 +567,7 @@ public class BackgroundInstallControlService extends SystemService {
        Looper getLooper();

        File getDiskFile();

    }

    private static final class InjectorImpl implements Injector {
@@ -568,11 +604,11 @@ public class BackgroundInstallControlService extends SystemService {

        @Override
        public Looper getLooper() {
            ServiceThread serviceThread = new ServiceThread(TAG,
                    android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */);
            ServiceThread serviceThread =
                    new ServiceThread(
                            TAG, android.os.Process.THREAD_PRIORITY_FOREGROUND, true /* allowIo */);
            serviceThread.start();
            return serviceThread.getLooper();

        }

        @Override
+0 −1
Original line number Diff line number Diff line
@@ -11,7 +11,6 @@
// 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
+16 −7
Original line number Diff line number Diff line
@@ -41,6 +41,10 @@ public final class BackgroundInstallControlServiceHostTest extends BaseHostJUnit
    private static final String MOCK_APK_FILE_1 = "BackgroundInstallControlMockApp1.apk";
    private static final String MOCK_APK_FILE_2 = "BackgroundInstallControlMockApp2.apk";

    // TODO: Move the silent installs to test-app using {@link
    // BackgroundInstallControlServiceTest#installPackage(String, String)} and remove deviceConfig
    // branch in BICS.
    // b/310983905
    @Test
    public void testGetMockBackgroundInstalledPackages() throws Exception {
        installPackage(TEST_DATA_DIR + MOCK_APK_FILE_1);
@@ -49,9 +53,14 @@ public final class BackgroundInstallControlServiceHostTest extends BaseHostJUnit
        assertThat(getDevice().getAppPackageInfo(MOCK_PACKAGE_NAME_1)).isNotNull();
        assertThat(getDevice().getAppPackageInfo(MOCK_PACKAGE_NAME_2)).isNotNull();

        assertThat(getDevice().setProperty("debug.transparency.bg-install-apps",
                    MOCK_PACKAGE_NAME_1 + "," + MOCK_PACKAGE_NAME_2)).isTrue();
        runDeviceTest("testGetMockBackgroundInstalledPackages");
        assertThat(
                getDevice()
                        .setProperty(
                                "debug.transparency.bg-install-apps",
                                MOCK_PACKAGE_NAME_1 + "," + MOCK_PACKAGE_NAME_2))
                .isTrue();
        runDeviceTest(
                "BackgroundInstallControlServiceTest", "testGetMockBackgroundInstalledPackages");
        assertThat(getDevice().uninstallPackage(MOCK_PACKAGE_NAME_1)).isNull();
        assertThat(getDevice().uninstallPackage(MOCK_PACKAGE_NAME_2)).isNull();

@@ -65,9 +74,9 @@ public final class BackgroundInstallControlServiceHostTest extends BaseHostJUnit
        assertThat(result.getStatus() == CommandStatus.SUCCESS).isTrue();
    }

    private void runDeviceTest(String method) throws DeviceNotAvailableException {
    private void runDeviceTest(String testName, String method) throws DeviceNotAvailableException {
        var options = new DeviceTestRunOptions(PACKAGE_NAME);
        options.setTestClassName(PACKAGE_NAME + ".BackgroundInstallControlServiceTest");
        options.setTestClassName(PACKAGE_NAME + "." + testName);
        options.setTestMethodName(method);
        runDeviceTests(options);
    }
+39 −11
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@

package com.android.server.pm.test.app;

import static android.Manifest.permission.GET_BACKGROUND_INSTALLED_PACKAGES;

import static com.android.compatibility.common.util.SystemUtil.runShellCommand;

import static com.google.common.truth.Truth.assertThat;

import android.content.Context;
@@ -23,12 +27,15 @@ import android.content.pm.IBackgroundInstallControlService;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;

import androidx.test.runner.AndroidJUnit4;

import com.android.compatibility.common.util.ShellIdentityUtils;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,30 +46,51 @@ import java.util.stream.Collectors;
@RunWith(AndroidJUnit4.class)
public class BackgroundInstallControlServiceTest {
    private static final String TAG = "BackgroundInstallControlServiceTest";
    private static final String MOCK_PACKAGE_NAME = "com.android.servicestests.apps.bicmockapp3";

    private IBackgroundInstallControlService mIBics;

    @Before
    public void setUp() {
        mIBics = IBackgroundInstallControlService.Stub.asInterface(
        mIBics =
                IBackgroundInstallControlService.Stub.asInterface(
                        ServiceManager.getService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE));
        assertThat(mIBics).isNotNull();
    }

    @After
    public void tearDown() {
        runShellCommand("pm uninstall " + MOCK_PACKAGE_NAME);
    }

    @Test
    public void testGetMockBackgroundInstalledPackages() throws RemoteException {
        ParceledListSlice<PackageInfo> slice = mIBics.getBackgroundInstalledPackages(
                    PackageManager.MATCH_ALL,
                    UserHandle.USER_ALL);
        ParceledListSlice<PackageInfo> slice =
                ShellIdentityUtils.invokeMethodWithShellPermissions(
                        mIBics,
                        (bics) -> {
                            try {
                                return bics.getBackgroundInstalledPackages(
                                        PackageManager.MATCH_ALL, Process.myUserHandle()
                                                .getIdentifier());
                            } catch (RemoteException e) {
                                throw new RuntimeException(e);
                            }
                        },
                        GET_BACKGROUND_INSTALLED_PACKAGES);
        assertThat(slice).isNotNull();

        var packageList = slice.getList();
        assertThat(packageList).isNotNull();
        assertThat(packageList).hasSize(2);

        var expectedPackageNames = Set.of("com.android.servicestests.apps.bicmockapp1",
        var expectedPackageNames =
                Set.of(
                        "com.android.servicestests.apps.bicmockapp1",
                        "com.android.servicestests.apps.bicmockapp2");
        var actualPackageNames = packageList.stream().map((packageInfo) -> packageInfo.packageName)
        var actualPackageNames =
                packageList.stream()
                        .map((packageInfo) -> packageInfo.packageName)
                        .collect(Collectors.toSet());
        assertThat(actualPackageNames).containsExactlyElementsIn(expectedPackageNames);
    }
Loading