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

Commit 3ecf5d8d authored by Ryan Mitchell's avatar Ryan Mitchell Committed by android-build-merger
Browse files

Merge "Stop idmap2d after several seconds pass" into qt-r1-dev

am: aa22db3d

Change-Id: I7da4c5bdf53cab99da6a07f85a3ec53f76678494
parents cb2e436c aa22db3d
Loading
Loading
Loading
Loading
+194 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.om;

import static android.content.Context.IDMAP_SERVICE;

import static com.android.server.om.OverlayManagerService.DEBUG;
import static com.android.server.om.OverlayManagerService.TAG;

import android.os.IBinder;
import android.os.IIdmap2;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.util.Slog;

import com.android.server.FgThread;

import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * To prevent idmap2d from continuously running, the idmap daemon will terminate after 10
 * seconds without a transaction.
 **/
class IdmapDaemon {
    // The amount of time in milliseconds to wait after a transaction to the idmap service is made
    // before stopping the service.
    private static final int SERVICE_TIMEOUT_MS = 10000;

    // The amount of time in milliseconds to wait when attempting to connect to idmap service.
    private static final int SERVICE_CONNECT_TIMEOUT_MS = 5000;

    private static final Object IDMAP_TOKEN = new Object();
    private static final String IDMAP_DAEMON = "idmap2d";

    private static IdmapDaemon sInstance;
    private volatile IIdmap2 mService;
    private final AtomicInteger mOpenedCount = new AtomicInteger();

    /**
     * An {@link AutoCloseable} connection to the idmap service. When the connection is closed or
     * finalized, the idmap service will be stopped after a period of time unless another connection
     * to the service is open.
     **/
    private class Connection implements AutoCloseable {
        private boolean mOpened = true;

        private Connection() {
            synchronized (IDMAP_TOKEN) {
                mOpenedCount.incrementAndGet();
            }
        }

        @Override
        public void close() {
            synchronized (IDMAP_TOKEN) {
                if (!mOpened) {
                    return;
                }

                mOpened = false;
                if (mOpenedCount.decrementAndGet() != 0) {
                    // Only post the callback to stop the service if the service does not have an
                    // open connection.
                    return;
                }

                FgThread.getHandler().postDelayed(() -> {
                    synchronized (IDMAP_TOKEN) {
                        // Only stop the service if the service does not have an open connection.
                        if (mService == null || mOpenedCount.get() != 0) {
                            return;
                        }

                        stopIdmapService();
                        mService = null;
                    }
                }, IDMAP_TOKEN, SERVICE_TIMEOUT_MS);
            }
        }
    }

    static IdmapDaemon getInstance() {
        if (sInstance == null) {
            sInstance = new IdmapDaemon();
        }
        return sInstance;
    }

    String createIdmap(String targetPath, String overlayPath, int policies, boolean enforce,
            int userId) throws Exception {
        try (Connection connection = connect()) {
            return mService.createIdmap(targetPath, overlayPath, policies, enforce, userId);
        }
    }

    boolean removeIdmap(String overlayPath, int userId) throws Exception {
        try (Connection connection = connect()) {
            return mService.removeIdmap(overlayPath, userId);
        }
    }

    boolean verifyIdmap(String overlayPath, int policies, boolean enforce, int userId)
            throws Exception {
        try (Connection connection = connect()) {
            return mService.verifyIdmap(overlayPath, policies, enforce, userId);
        }
    }

    String getIdmapPath(String overlayPath, int userId) throws Exception {
        try (Connection connection = connect()) {
            return mService.getIdmapPath(overlayPath, userId);
        }
    }

    static void startIdmapService() {
        SystemProperties.set("ctl.start", IDMAP_DAEMON);
    }

    static void stopIdmapService() {
        SystemProperties.set("ctl.stop", IDMAP_DAEMON);
    }

    private Connection connect() throws Exception {
        synchronized (IDMAP_TOKEN) {
            FgThread.getHandler().removeCallbacksAndMessages(IDMAP_TOKEN);
            if (mService != null) {
                // Not enough time has passed to stop the idmap service. Reuse the existing
                // interface.
                return new Connection();
            }

            // Start the idmap service if it is not currently running.
            startIdmapService();

            // Block until the service is found.
            FutureTask<IBinder> bindIdmap = new FutureTask<>(() -> {
                while (true) {
                    try {
                        IBinder binder = ServiceManager.getService(IDMAP_SERVICE);
                        if (binder != null) {
                            return binder;
                        }
                    } catch (Exception e) {
                        Slog.e(TAG, "service '" + IDMAP_SERVICE + "' not retrieved; "
                                + e.getMessage());
                    }
                    Thread.sleep(100);
                }
            });

            IBinder binder;
            try {
                FgThread.getHandler().postAtFrontOfQueue(bindIdmap);
                binder = bindIdmap.get(SERVICE_CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
            } catch (Exception rethrow) {
                Slog.e(TAG, "service '" + IDMAP_SERVICE + "' not found;");
                throw rethrow;
            }

            try {
                binder.linkToDeath(() -> {
                    Slog.w(TAG, "service '" + IDMAP_SERVICE + "' died");
                }, 0);
            } catch (RemoteException rethrow) {
                Slog.e(TAG, "service '" + IDMAP_SERVICE + "' failed to be bound");
                throw rethrow;
            }

            mService = IIdmap2.Stub.asInterface(binder);
            if (DEBUG) {
                Slog.d(TAG, "service '" + IDMAP_SERVICE + "' connected");
            }

            return new Connection();
        }
    }
}
+10 −49
Original line number Diff line number Diff line
@@ -16,9 +16,6 @@

package com.android.server.om;

import static android.content.Context.IDMAP_SERVICE;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;

import static com.android.server.om.OverlayManagerService.DEBUG;
import static com.android.server.om.OverlayManagerService.TAG;

@@ -27,15 +24,11 @@ import android.content.om.OverlayInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.os.Build.VERSION_CODES;
import android.os.IBinder;
import android.os.IIdmap2;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.Slog;

import com.android.internal.os.BackgroundThread;
import com.android.server.om.OverlayManagerServiceImpl.PackageManagerHelper;
import com.android.server.pm.Installer;

@@ -51,11 +44,6 @@ import java.io.File;
 */
class IdmapManager {
    private static final boolean FEATURE_FLAG_IDMAP2 = true;

    private final Installer mInstaller;
    private final PackageManagerHelper mPackageManager;
    private IIdmap2 mIdmap2Service;

    private static final boolean VENDOR_IS_Q_OR_LATER;
    static {
        final String value = SystemProperties.get("ro.vndk.version", "29");
@@ -70,12 +58,14 @@ class IdmapManager {
        VENDOR_IS_Q_OR_LATER = isQOrLater;
    }

    private final Installer mInstaller;
    private final PackageManagerHelper mPackageManager;
    private final IdmapDaemon mIdmapDaemon;

    IdmapManager(final Installer installer, final PackageManagerHelper packageManager) {
        mInstaller = installer;
        mPackageManager = packageManager;
        if (FEATURE_FLAG_IDMAP2) {
            connectToIdmap2d();
        }
        mIdmapDaemon = IdmapDaemon.getInstance();
    }

    boolean createIdmap(@NonNull final PackageInfo targetPackage,
@@ -91,11 +81,11 @@ class IdmapManager {
            if (FEATURE_FLAG_IDMAP2) {
                int policies = calculateFulfilledPolicies(targetPackage, overlayPackage, userId);
                boolean enforce = enforceOverlayable(overlayPackage);
                if (mIdmap2Service.verifyIdmap(overlayPath, policies, enforce, userId)) {
                if (mIdmapDaemon.verifyIdmap(overlayPath, policies, enforce, userId)) {
                    return true;
                }
                return mIdmap2Service.createIdmap(targetPath, overlayPath, policies, enforce,
                    userId) != null;
                return mIdmapDaemon.createIdmap(targetPath, overlayPath, policies,
                        enforce, userId) != null;
            } else {
                mInstaller.idmap(targetPath, overlayPath, sharedGid);
                return true;
@@ -113,7 +103,7 @@ class IdmapManager {
        }
        try {
            if (FEATURE_FLAG_IDMAP2) {
                return mIdmap2Service.removeIdmap(oi.baseCodePath, userId);
                return mIdmapDaemon.removeIdmap(oi.baseCodePath, userId);
            } else {
                mInstaller.removeIdmap(oi.baseCodePath);
                return true;
@@ -137,7 +127,7 @@ class IdmapManager {
            final int userId) {
        if (FEATURE_FLAG_IDMAP2) {
            try {
                return mIdmap2Service.getIdmapPath(overlayPackagePath, userId);
                return mIdmapDaemon.getIdmapPath(overlayPackagePath, userId);
            } catch (Exception e) {
                Slog.w(TAG, "failed to get idmap path for " + overlayPackagePath + ": "
                        + e.getMessage());
@@ -151,35 +141,6 @@ class IdmapManager {
        }
    }

    private void connectToIdmap2d() {
        IBinder binder = ServiceManager.getService(IDMAP_SERVICE);
        if (binder != null) {
            try {
                binder.linkToDeath(new IBinder.DeathRecipient() {
                    @Override
                    public void binderDied() {
                        Slog.w(TAG, "service '" + IDMAP_SERVICE + "' died; reconnecting...");
                        connectToIdmap2d();
                    }

                }, 0);
            } catch (RemoteException e) {
                binder = null;
            }
        }
        if (binder != null) {
            mIdmap2Service = IIdmap2.Stub.asInterface(binder);
            if (DEBUG) {
                Slog.d(TAG, "service '" + IDMAP_SERVICE + "' connected");
            }
        } else {
            Slog.w(TAG, "service '" + IDMAP_SERVICE + "' not found; trying again...");
            BackgroundThread.getHandler().postDelayed(() -> {
                connectToIdmap2d();
            }, SECOND_IN_MILLIS);
        }
    }

    /**
     * Checks if overlayable and policies should be enforced on the specified overlay for backwards
     * compatibility with pre-Q overlays.
+1 −0
Original line number Diff line number Diff line
@@ -262,6 +262,7 @@ public final class OverlayManagerService extends SystemService {

            initIfNeeded();
            onSwitchUser(UserHandle.USER_SYSTEM);
            IdmapDaemon.stopIdmapService();

            publishBinderService(Context.OVERLAY_SERVICE, mService);
            publishLocalService(OverlayManagerService.class, this);