Loading services/core/java/com/android/server/om/IdmapDaemon.java 0 → 100644 +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(); } } } services/core/java/com/android/server/om/IdmapManager.java +10 −49 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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"); Loading @@ -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, Loading @@ -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; Loading @@ -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; Loading @@ -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()); Loading @@ -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. Loading services/core/java/com/android/server/om/OverlayManagerService.java +1 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading
services/core/java/com/android/server/om/IdmapDaemon.java 0 → 100644 +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(); } } }
services/core/java/com/android/server/om/IdmapManager.java +10 −49 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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"); Loading @@ -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, Loading @@ -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; Loading @@ -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; Loading @@ -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()); Loading @@ -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. Loading
services/core/java/com/android/server/om/OverlayManagerService.java +1 −0 Original line number Diff line number Diff line Loading @@ -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); Loading