diff options
| author | 2019-08-07 10:34:06 -0700 | |
|---|---|---|
| committer | 2019-08-07 10:34:06 -0700 | |
| commit | dfa44e1776a8485fe1c94086ee9a6d02c5186c8c (patch) | |
| tree | e6446303448a03ad8ff5f8b9c654bc51673880ab | |
| parent | a4172900400f98fd49c1976a429124d44de56a41 (diff) | |
| parent | e4521f7775911cb8d8e355927c3dd394f67edfcb (diff) | |
Merge "Stop idmap2d after several seconds pass" into qt-r1-dev am: aa22db3da7 am: 6f66d1683d
am: e4521f7775
Change-Id: I8b18b2965a75278264587831c24ead01f4c126cd
3 files changed, 205 insertions, 49 deletions
| diff --git a/services/core/java/com/android/server/om/IdmapDaemon.java b/services/core/java/com/android/server/om/IdmapDaemon.java new file mode 100644 index 000000000000..91824c3e9f43 --- /dev/null +++ b/services/core/java/com/android/server/om/IdmapDaemon.java @@ -0,0 +1,194 @@ +/* + * 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(); +        } +    } +} diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java index 1f20968ca0c8..288ef0e618b6 100644 --- a/services/core/java/com/android/server/om/IdmapManager.java +++ b/services/core/java/com/android/server/om/IdmapManager.java @@ -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. diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index da69986cd59f..ce951816d3a6 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -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); |