Loading core/java/android/content/pm/IBackgroundInstallControlService.aidl +5 −0 Original line number Diff line number Diff line Loading @@ -17,10 +17,15 @@ package android.content.pm; import android.content.pm.ParceledListSlice; import android.os.IRemoteCallback; /** * {@hide} */ interface IBackgroundInstallControlService { ParceledListSlice getBackgroundInstalledPackages(long flags, int userId); void registerBackgroundInstallCallback(IRemoteCallback callback); void unregisterBackgroundInstallCallback(IRemoteCallback callback); } services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java 0 → 100644 +97 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.pm; import static android.os.Process.THREAD_PRIORITY_BACKGROUND; import android.annotation.NonNull; import android.app.BackgroundInstallControlManager; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.IRemoteCallback; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.server.ServiceThread; public class BackgroundInstallControlCallbackHelper { @VisibleForTesting static final String FLAGGED_PACKAGE_NAME_KEY = "packageName"; @VisibleForTesting static final String FLAGGED_USER_ID_KEY = "userId"; private static final String TAG = "BackgroundInstallControlCallbackHelper"; private final Handler mHandler; BackgroundInstallControlCallbackHelper() { HandlerThread backgroundThread = new ServiceThread( "BackgroundInstallControlCallbackHelperBg", THREAD_PRIORITY_BACKGROUND, true); backgroundThread.start(); mHandler = new Handler(backgroundThread.getLooper()); } @NonNull @VisibleForTesting final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>(); /** Registers callback that gets invoked upon detection of an MBA * * NOTE: The callback is user context agnostic and currently broadcasts to all users of other * users app installs. This is fine because the API is for SystemServer use only. */ public void registerBackgroundInstallCallback(IRemoteCallback callback) { synchronized (mCallbacks) { mCallbacks.register(callback, null); } } /** Unregisters callback */ public void unregisterBackgroundInstallCallback(IRemoteCallback callback) { synchronized (mCallbacks) { mCallbacks.unregister(callback); } } /** * Invokes all registered callbacks Callbacks are processed through user provided-threads and * parameters are passed in via {@link BackgroundInstallControlManager} InstallEvent */ public void notifyAllCallbacks(int userId, String packageName) { Bundle extras = new Bundle(); extras.putCharSequence(FLAGGED_PACKAGE_NAME_KEY, packageName); extras.putInt(FLAGGED_USER_ID_KEY, userId); synchronized (mCallbacks) { mHandler.post( () -> mCallbacks.broadcast( callback -> { try { callback.sendResult(extras); } catch (RemoteException e) { Slog.e( TAG, "error detected: " + e.getLocalizedMessage(), e); } })); } } } services/core/java/com/android/server/pm/BackgroundInstallControlService.java +22 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.os.Binder; import android.os.Build; import android.os.Environment; import android.os.Handler; import android.os.IRemoteCallback; import android.os.Looper; import android.os.Message; import android.os.SystemClock; Loading Loading @@ -93,6 +94,8 @@ public class BackgroundInstallControlService extends SystemService { private final File mDiskFile; private final Context mContext; private final BackgroundInstallControlCallbackHelper mCallbackHelper; private SparseSetArray<String> mBackgroundInstalledPackages = null; // User ID -> package name -> set of foreground time frame Loading @@ -112,6 +115,7 @@ public class BackgroundInstallControlService extends SystemService { mHandler = new EventHandler(injector.getLooper(), this); mDiskFile = injector.getDiskFile(); mContext = injector.getContext(); mCallbackHelper = injector.getBackgroundInstallControlCallbackHelper(); UsageStatsManagerInternal usageStatsManagerInternal = injector.getUsageStatsManagerInternal(); usageStatsManagerInternal.registerListener( Loading Loading @@ -150,6 +154,16 @@ public class BackgroundInstallControlService extends SystemService { } } @Override public void registerBackgroundInstallCallback(IRemoteCallback callback) { mService.mCallbackHelper.registerBackgroundInstallCallback(callback); } @Override public void unregisterBackgroundInstallCallback(IRemoteCallback callback) { mService.mCallbackHelper.unregisterBackgroundInstallCallback(callback); } } @RequiresPermission(GET_BACKGROUND_INSTALLED_PACKAGES) Loading Loading @@ -274,6 +288,7 @@ public class BackgroundInstallControlService extends SystemService { initBackgroundInstalledPackages(); mBackgroundInstalledPackages.add(userId, packageName); mCallbackHelper.notifyAllCallbacks(userId, packageName); writeBackgroundInstalledPackagesToDisk(); } Loading Loading @@ -568,6 +583,8 @@ public class BackgroundInstallControlService extends SystemService { File getDiskFile(); BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper(); } private static final class InjectorImpl implements Injector { Loading Loading @@ -617,5 +634,10 @@ public class BackgroundInstallControlService extends SystemService { File file = new File(dir, DISK_FILE_NAME); return file; } @Override public BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper() { return new BackgroundInstallControlCallbackHelper(); } } } services/tests/BackgroundInstallControlServiceTests/host/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ java_test_host { ":BackgroundInstallControlServiceTestApp", ":BackgroundInstallControlMockApp1", ":BackgroundInstallControlMockApp2", ":BackgroundInstallControlMockApp3", ], test_suites: [ "general-tests", Loading services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml +3 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,9 @@ <option name="push-file" key="BackgroundInstallControlMockApp2.apk" value="/data/local/tmp/BackgroundInstallControlMockApp2.apk" /> <option name="push-file" key="BackgroundInstallControlMockApp3.apk" value="/data/local/tmp/BackgroundInstallControlMockApp3.apk" /> </target_preparer> <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" > Loading Loading
core/java/android/content/pm/IBackgroundInstallControlService.aidl +5 −0 Original line number Diff line number Diff line Loading @@ -17,10 +17,15 @@ package android.content.pm; import android.content.pm.ParceledListSlice; import android.os.IRemoteCallback; /** * {@hide} */ interface IBackgroundInstallControlService { ParceledListSlice getBackgroundInstalledPackages(long flags, int userId); void registerBackgroundInstallCallback(IRemoteCallback callback); void unregisterBackgroundInstallCallback(IRemoteCallback callback); }
services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java 0 → 100644 +97 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.pm; import static android.os.Process.THREAD_PRIORITY_BACKGROUND; import android.annotation.NonNull; import android.app.BackgroundInstallControlManager; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.IRemoteCallback; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.server.ServiceThread; public class BackgroundInstallControlCallbackHelper { @VisibleForTesting static final String FLAGGED_PACKAGE_NAME_KEY = "packageName"; @VisibleForTesting static final String FLAGGED_USER_ID_KEY = "userId"; private static final String TAG = "BackgroundInstallControlCallbackHelper"; private final Handler mHandler; BackgroundInstallControlCallbackHelper() { HandlerThread backgroundThread = new ServiceThread( "BackgroundInstallControlCallbackHelperBg", THREAD_PRIORITY_BACKGROUND, true); backgroundThread.start(); mHandler = new Handler(backgroundThread.getLooper()); } @NonNull @VisibleForTesting final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>(); /** Registers callback that gets invoked upon detection of an MBA * * NOTE: The callback is user context agnostic and currently broadcasts to all users of other * users app installs. This is fine because the API is for SystemServer use only. */ public void registerBackgroundInstallCallback(IRemoteCallback callback) { synchronized (mCallbacks) { mCallbacks.register(callback, null); } } /** Unregisters callback */ public void unregisterBackgroundInstallCallback(IRemoteCallback callback) { synchronized (mCallbacks) { mCallbacks.unregister(callback); } } /** * Invokes all registered callbacks Callbacks are processed through user provided-threads and * parameters are passed in via {@link BackgroundInstallControlManager} InstallEvent */ public void notifyAllCallbacks(int userId, String packageName) { Bundle extras = new Bundle(); extras.putCharSequence(FLAGGED_PACKAGE_NAME_KEY, packageName); extras.putInt(FLAGGED_USER_ID_KEY, userId); synchronized (mCallbacks) { mHandler.post( () -> mCallbacks.broadcast( callback -> { try { callback.sendResult(extras); } catch (RemoteException e) { Slog.e( TAG, "error detected: " + e.getLocalizedMessage(), e); } })); } } }
services/core/java/com/android/server/pm/BackgroundInstallControlService.java +22 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.os.Binder; import android.os.Build; import android.os.Environment; import android.os.Handler; import android.os.IRemoteCallback; import android.os.Looper; import android.os.Message; import android.os.SystemClock; Loading Loading @@ -93,6 +94,8 @@ public class BackgroundInstallControlService extends SystemService { private final File mDiskFile; private final Context mContext; private final BackgroundInstallControlCallbackHelper mCallbackHelper; private SparseSetArray<String> mBackgroundInstalledPackages = null; // User ID -> package name -> set of foreground time frame Loading @@ -112,6 +115,7 @@ public class BackgroundInstallControlService extends SystemService { mHandler = new EventHandler(injector.getLooper(), this); mDiskFile = injector.getDiskFile(); mContext = injector.getContext(); mCallbackHelper = injector.getBackgroundInstallControlCallbackHelper(); UsageStatsManagerInternal usageStatsManagerInternal = injector.getUsageStatsManagerInternal(); usageStatsManagerInternal.registerListener( Loading Loading @@ -150,6 +154,16 @@ public class BackgroundInstallControlService extends SystemService { } } @Override public void registerBackgroundInstallCallback(IRemoteCallback callback) { mService.mCallbackHelper.registerBackgroundInstallCallback(callback); } @Override public void unregisterBackgroundInstallCallback(IRemoteCallback callback) { mService.mCallbackHelper.unregisterBackgroundInstallCallback(callback); } } @RequiresPermission(GET_BACKGROUND_INSTALLED_PACKAGES) Loading Loading @@ -274,6 +288,7 @@ public class BackgroundInstallControlService extends SystemService { initBackgroundInstalledPackages(); mBackgroundInstalledPackages.add(userId, packageName); mCallbackHelper.notifyAllCallbacks(userId, packageName); writeBackgroundInstalledPackagesToDisk(); } Loading Loading @@ -568,6 +583,8 @@ public class BackgroundInstallControlService extends SystemService { File getDiskFile(); BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper(); } private static final class InjectorImpl implements Injector { Loading Loading @@ -617,5 +634,10 @@ public class BackgroundInstallControlService extends SystemService { File file = new File(dir, DISK_FILE_NAME); return file; } @Override public BackgroundInstallControlCallbackHelper getBackgroundInstallControlCallbackHelper() { return new BackgroundInstallControlCallbackHelper(); } } }
services/tests/BackgroundInstallControlServiceTests/host/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ java_test_host { ":BackgroundInstallControlServiceTestApp", ":BackgroundInstallControlMockApp1", ":BackgroundInstallControlMockApp2", ":BackgroundInstallControlMockApp3", ], test_suites: [ "general-tests", Loading
services/tests/BackgroundInstallControlServiceTests/host/AndroidTest.xml +3 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,9 @@ <option name="push-file" key="BackgroundInstallControlMockApp2.apk" value="/data/local/tmp/BackgroundInstallControlMockApp2.apk" /> <option name="push-file" key="BackgroundInstallControlMockApp3.apk" value="/data/local/tmp/BackgroundInstallControlMockApp3.apk" /> </target_preparer> <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" > Loading