From 6a735d20646420bd80181b400af284414edb8721 Mon Sep 17 00:00:00 2001 From: Howard Wu <40033067+Howard20181@users.noreply.github.com> Date: Sat, 4 Dec 2021 21:44:40 +0800 Subject: [PATCH] Reload ModulesFragment after user removed or added (#1465) Co-authored-by: LoveSy --- .../main/java/org/lsposed/manager/App.java | 42 +++++-- .../lsposed/manager/adapters/AppHelper.java | 3 + .../manager/adapters/ScopeAdapter.java | 1 + .../manager/ui/fragment/ModulesFragment.java | 105 +++++++++++------- .../org/lsposed/manager/util/ModuleUtil.java | 11 ++ .../lspd/service/ActivityManagerService.java | 12 +- .../lspd/service/LSPManagerService.java | 8 +- .../lsposed/lspd/service/LSPosedService.java | 78 ++++++++++--- 8 files changed, 185 insertions(+), 75 deletions(-) diff --git a/app/src/main/java/org/lsposed/manager/App.java b/app/src/main/java/org/lsposed/manager/App.java index 1851a1e3..85f5cccc 100644 --- a/app/src/main/java/org/lsposed/manager/App.java +++ b/app/src/main/java/org/lsposed/manager/App.java @@ -20,6 +20,7 @@ package org.lsposed.manager; +import android.annotation.SuppressLint; import android.app.Application; import android.content.BroadcastReceiver; import android.content.Context; @@ -116,6 +117,10 @@ public class App extends Application { } public static final String TAG = "LSPosedManager"; + private static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED"; + private static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED"; + private static final String ACTION_USER_INFO_CHANGED = "android.intent.action.USER_INFO_CHANGED"; + private static final String EXTRA_REMOVED_FOR_ALL_USERS = "android.intent.extra.REMOVED_FOR_ALL_USERS"; private static App instance = null; private static OkHttpClient okHttpClient; private static Cache okHttpCache; @@ -138,6 +143,7 @@ public class App extends Application { return !Process.isApplicationUid(Process.myUid()); } + @SuppressLint("WrongConstant") private void setCrashReport() { Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> { @@ -182,17 +188,39 @@ public class App extends Application { DayNightDelegate.setDefaultNightMode(ThemeUtil.getDarkTheme()); LocaleDelegate.setDefaultLocale(getLocale()); + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction("org.lsposed.manager.NOTIFICATION"); registerReceiver(new BroadcastReceiver() { @Override - public void onReceive(Context context, Intent intent) { - int userId = intent.getIntExtra(Intent.EXTRA_USER, 0); - String packageName = intent.getStringExtra("android.intent.extra.PACKAGES"); - boolean packageFullyRemoved = intent.getBooleanExtra(Intent.ACTION_PACKAGE_FULLY_REMOVED, false); - if (packageName != null) { - ModuleUtil.getInstance().reloadSingleModule(packageName, userId, packageFullyRemoved); + public void onReceive(Context context, Intent inIntent) { + var intent = (Intent) inIntent.getParcelableExtra(Intent.EXTRA_INTENT); + Log.d(TAG, "onReceive: " + intent); + switch (intent.getAction()) { + case Intent.ACTION_PACKAGE_ADDED: + case Intent.ACTION_PACKAGE_CHANGED: + case Intent.ACTION_PACKAGE_FULLY_REMOVED: + case Intent.ACTION_UID_REMOVED: { + var userId = intent.getIntExtra(Intent.EXTRA_USER, 0); + var packageName = intent.getStringExtra("android.intent.extra.PACKAGES"); + var packageRemovedForAllUsers = intent.getBooleanExtra(EXTRA_REMOVED_FOR_ALL_USERS, false); + var isXposedModule = intent.getBooleanExtra("isXposedModule", false); + if (packageName != null) { + if (isXposedModule) + ModuleUtil.getInstance().reloadSingleModule(packageName, userId, packageRemovedForAllUsers); + else + App.getExecutorService().submit(() -> AppHelper.getAppList(true)); + } + break; + } + case ACTION_USER_ADDED: + case ACTION_USER_REMOVED: + case ACTION_USER_INFO_CHANGED: { + App.getExecutorService().submit(() -> ModuleUtil.getInstance().reloadInstalledModules()); + break; + } } } - }, new IntentFilter(Intent.ACTION_PACKAGE_CHANGED)); + }, intentFilter); UpdateUtil.loadRemoteVersion(); diff --git a/app/src/main/java/org/lsposed/manager/adapters/AppHelper.java b/app/src/main/java/org/lsposed/manager/adapters/AppHelper.java index c184a28d..42142351 100644 --- a/app/src/main/java/org/lsposed/manager/adapters/AppHelper.java +++ b/app/src/main/java/org/lsposed/manager/adapters/AppHelper.java @@ -20,6 +20,7 @@ package org.lsposed.manager.adapters; +import android.annotation.SuppressLint; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; @@ -44,6 +45,7 @@ public class AppHelper { private static List appList; private static final ConcurrentHashMap appLabel = new ConcurrentHashMap<>(); + @SuppressLint("WrongConstant") public static Intent getSettingsIntent(String packageName, int userId) { Intent intentToResolve = new Intent(Intent.ACTION_MAIN); intentToResolve.addCategory(SETTINGS_CATEGORY); @@ -63,6 +65,7 @@ public class AppHelper { return intent; } + @SuppressLint("WrongConstant") public static Intent getLaunchIntentForPackage(String packageName, int userId) { Intent intentToResolve = new Intent(Intent.ACTION_MAIN); intentToResolve.addCategory(Intent.CATEGORY_INFO); diff --git a/app/src/main/java/org/lsposed/manager/adapters/ScopeAdapter.java b/app/src/main/java/org/lsposed/manager/adapters/ScopeAdapter.java index 827950d6..49a0066e 100644 --- a/app/src/main/java/org/lsposed/manager/adapters/ScopeAdapter.java +++ b/app/src/main/java/org/lsposed/manager/adapters/ScopeAdapter.java @@ -266,6 +266,7 @@ public class ScopeAdapter extends EmptyStateRecyclerView.EmptyStateAdapter users = ConfigManager.getUsers(); protected FragmentPagerBinding binding; protected SearchView searchView; private SearchView.OnQueryTextListener searchListener; - final ArrayList adapters = new ArrayList<>(); + SparseArray adapters = new SparseArray<>(); + PagerAdapter pagerAdapter = null; private ModuleUtil.InstalledModule selectedModule; @@ -110,24 +110,22 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi searchListener = new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { - adapters.forEach(adapter -> adapter.getFilter().filter(query)); + forEachAdaptor(adapter -> adapter.getFilter().filter(query)); return false; } @Override public boolean onQueryTextChange(String query) { - adapters.forEach(adapter -> adapter.getFilter().filter(query)); + forEachAdaptor(adapter -> adapter.getFilter().filter(query)); return false; } }; + } - if (users != null) { - for (var user : users) { - var adapter = new ModuleAdapter(user); - adapter.setHasStableIds(true); - adapter.setStateRestorationPolicy(PREVENT_WHEN_EMPTY); - adapters.add(adapter); - } + private void forEachAdaptor(Consumer action) { + var snapshot = adapters; + for (var i = 0; i < snapshot.size(); ++i) { + action.accept(snapshot.valueAt(i)); } } @@ -149,7 +147,8 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi binding = FragmentPagerBinding.inflate(inflater, container, false); binding.appBar.setLiftable(true); setupToolbar(binding.toolbar, binding.clickView, R.string.Modules, R.menu.menu_modules); - binding.viewPager.setAdapter(new PagerAdapter(this)); + pagerAdapter = new PagerAdapter(this); + binding.viewPager.setAdapter(pagerAdapter); binding.viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override public void onPageSelected(int position) { @@ -159,29 +158,22 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi new TabLayoutMediator(binding.tabLayout, binding.viewPager, (tab, position) -> { if (position < adapters.size()) { - tab.setText(adapters.get(position).getUser().name); + tab.setText(adapters.valueAt(position).getUser().name); } }).attach(); - if (users != null && users.size() != 1) { - binding.viewPager.setUserInputEnabled(true); - binding.tabLayout.setVisibility(View.VISIBLE); - binding.tabLayout.addOnLayoutChangeListener((view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { - ViewGroup vg = (ViewGroup) binding.tabLayout.getChildAt(0); - int tabLayoutWidth = IntStream.range(0, binding.tabLayout.getTabCount()).map(i -> vg.getChildAt(i).getWidth()).sum(); - if (tabLayoutWidth <= binding.getRoot().getWidth()) { - binding.tabLayout.setTabMode(TabLayout.MODE_FIXED); - binding.tabLayout.setTabGravity(TabLayout.GRAVITY_FILL); - } - }); - binding.fab.show(); - } else { - binding.viewPager.setUserInputEnabled(false); - binding.tabLayout.setVisibility(View.GONE); - } + binding.tabLayout.addOnLayoutChangeListener((view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { + ViewGroup vg = (ViewGroup) binding.tabLayout.getChildAt(0); + int tabLayoutWidth = IntStream.range(0, binding.tabLayout.getTabCount()).map(i -> vg.getChildAt(i).getWidth()).sum(); + if (tabLayoutWidth <= binding.getRoot().getWidth()) { + binding.tabLayout.setTabMode(TabLayout.MODE_FIXED); + binding.tabLayout.setTabGravity(TabLayout.GRAVITY_FILL); + } + }); + binding.fab.setOnClickListener(v -> { var bundle = new Bundle(); - var user = adapters.get(binding.viewPager.getCurrentItem()).getUser(); + var user = adapters.valueAt(binding.viewPager.getCurrentItem()).getUser(); bundle.putParcelable("userInfo", user); var f = new RecyclerViewDialogFragment(); f.setArguments(bundle); @@ -190,7 +182,7 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi moduleUtil.addListener(this); repoLoader.addListener(this); - updateModuleSummary(); + onModulesReloaded(); return binding.getRoot(); } @@ -214,23 +206,48 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi @Override public void onResume() { super.onResume(); - adapters.forEach(ModuleAdapter::refresh); + forEachAdaptor(ModuleAdapter::refresh); } @Override public void onSingleModuleReloaded(ModuleUtil.InstalledModule module) { - adapters.forEach(ModuleAdapter::refresh); + forEachAdaptor(ModuleAdapter::refresh); } @Override public void onModulesReloaded() { - adapters.forEach(ModuleAdapter::refresh); + var users = moduleUtil.getUsers(); + if (users == null) return; + + if (users.size() != 1) { + binding.viewPager.setUserInputEnabled(true); + binding.tabLayout.setVisibility(View.VISIBLE); + binding.fab.show(); + } else { + binding.viewPager.setUserInputEnabled(false); + binding.tabLayout.setVisibility(View.GONE); + } + + var tmp = new SparseArray(users.size()); + var snapshot = adapters; + for (var user : users) { + if (snapshot.indexOfKey(user.id) >= 0) { + tmp.put(user.id, snapshot.get(user.id)); + } else { + var adapter = new ModuleAdapter(user); + adapter.setHasStableIds(true); + tmp.put(user.id, adapter); + } + } + adapters = tmp; + forEachAdaptor(ModuleAdapter::refresh); + runOnUiThread(pagerAdapter::notifyDataSetChanged); updateModuleSummary(); } @Override public void onRepoLoaded() { - adapters.forEach(ModuleAdapter::refresh); + forEachAdaptor(ModuleAdapter::refresh); } private void updateModuleSummary() { @@ -261,6 +278,7 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi .show(); } + @SuppressLint("WrongConstant") @Override public boolean onContextItemSelected(@NonNull MenuItem item) { if (selectedModule == null) { @@ -348,9 +366,9 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi if (fragment == null || arguments == null) { return null; } - int position = arguments.getInt("position"); + int userId = arguments.getInt("user_id"); binding = SwiperefreshRecyclerviewBinding.inflate(getLayoutInflater(), container, false); - adapter = fragment.adapters.get(position); + adapter = fragment.adapters.get(userId); binding.recyclerView.setAdapter(adapter); binding.recyclerView.setLayoutManager(new LinearLayoutManager(requireActivity())); binding.swipeRefreshLayout.setOnRefreshListener(adapter::fullRefresh); @@ -430,7 +448,7 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi @Override public Fragment createFragment(int position) { Bundle bundle = new Bundle(); - bundle.putInt("position", position); + bundle.putInt("user_id", adapters.keyAt(position)); Fragment fragment = new ModuleListFragment(); fragment.setArguments(bundle); return fragment; @@ -443,7 +461,12 @@ public class ModulesFragment extends BaseFragment implements ModuleUtil.ModuleLi @Override public long getItemId(int position) { - return position; + return adapters.keyAt(position); + } + + @Override + public boolean containsItem(long itemId) { + return adapters.indexOfKey((int) itemId) >= 0; } } diff --git a/app/src/main/java/org/lsposed/manager/util/ModuleUtil.java b/app/src/main/java/org/lsposed/manager/util/ModuleUtil.java index 7f2d9c80..bc3c9d0d 100644 --- a/app/src/main/java/org/lsposed/manager/util/ModuleUtil.java +++ b/app/src/main/java/org/lsposed/manager/util/ModuleUtil.java @@ -30,11 +30,13 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.util.Pair; +import org.lsposed.lspd.models.UserInfo; import org.lsposed.manager.App; import org.lsposed.manager.ConfigManager; import org.lsposed.manager.repo.RepoLoader; import org.lsposed.manager.repo.model.OnlineModule; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; @@ -50,6 +52,7 @@ public final class ModuleUtil { private final PackageManager pm; private final Set listeners = ConcurrentHashMap.newKeySet(); private HashSet enabledModules = new HashSet<>(); + private List users = new ArrayList<>(); private Map, InstalledModule> installedModules = new HashMap<>(); private boolean modulesLoaded = false; @@ -89,6 +92,7 @@ public final class ModuleUtil { } Map, InstalledModule> modules = new HashMap<>(); + var users = ConfigManager.getUsers(); for (PackageInfo pkg : ConfigManager.getInstalledPackagesFromAllUsers(PackageManager.GET_META_DATA, false)) { ApplicationInfo app = pkg.applicationInfo; @@ -100,11 +104,18 @@ public final class ModuleUtil { installedModules = modules; + this.users = users; + enabledModules = new HashSet<>(Arrays.asList(ConfigManager.getEnabledModules())); modulesLoaded = true; listeners.forEach(ModuleListener::onModulesReloaded); } + @Nullable + public List getUsers() { + return modulesLoaded ? users : null; + } + public InstalledModule reloadSingleModule(String packageName, int userId) { return reloadSingleModule(packageName, userId, false); } diff --git a/core/src/main/java/org/lsposed/lspd/service/ActivityManagerService.java b/core/src/main/java/org/lsposed/lspd/service/ActivityManagerService.java index 8b16d316..51a80e64 100644 --- a/core/src/main/java/org/lsposed/lspd/service/ActivityManagerService.java +++ b/core/src/main/java/org/lsposed/lspd/service/ActivityManagerService.java @@ -21,6 +21,7 @@ package org.lsposed.lspd.service; import static org.lsposed.lspd.service.ServiceManager.TAG; +import android.annotation.SuppressLint; import android.app.IActivityManager; import android.app.IApplicationThread; import android.app.IServiceConnection; @@ -101,17 +102,16 @@ public class ActivityManagerService { return am.startUserInBackground(userId); } + @SuppressLint("NewApi") public static Intent registerReceiver(String callerPackage, String callingFeatureId, IIntentReceiver receiver, IntentFilter filter, String requiredPermission, int userId, int flags) throws RemoteException { IActivityManager am = getActivityManager(); if (am == null || thread == null) return null; - try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S || (Build.VERSION.SDK_INT == Build.VERSION_CODES.R && Build.VERSION.PREVIEW_SDK_INT != 0)) - return am.registerReceiverWithFeature(thread, callerPackage, callingFeatureId, "null", receiver, filter, requiredPermission, userId, flags); - } catch (Throwable ignored) { - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && Build.VERSION.PREVIEW_SDK_INT != 0)) + return am.registerReceiverWithFeature(thread, callerPackage, callingFeatureId, "null", receiver, filter, requiredPermission, userId, flags); + else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { return am.registerReceiverWithFeature(thread, callerPackage, callingFeatureId, receiver, filter, requiredPermission, userId, flags); } else { return am.registerReceiver(thread, callerPackage, receiver, filter, requiredPermission, userId, flags); diff --git a/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java b/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java index 05f24745..249b545a 100644 --- a/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java +++ b/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java @@ -252,13 +252,11 @@ public class LSPManagerService extends ILSPManagerService.Stub { } @SuppressLint("WrongConstant") - public static void broadcastIntent(String modulePackageName, int moduleUserId, boolean packageFullyRemoved) { - Intent intent = new Intent(Intent.ACTION_PACKAGE_CHANGED); + public static void broadcastIntent(Intent inIntent) { + var intent = new Intent("org.lsposed.manager.NOTIFICATION"); + intent.putExtra(Intent.EXTRA_INTENT, inIntent); intent.addFlags(0x01000000); //Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND intent.addFlags(0x00400000); //Intent.FLAG_RECEIVER_FROM_SHELL - intent.putExtra("android.intent.extra.PACKAGES", modulePackageName); - intent.putExtra(Intent.EXTRA_USER, moduleUserId); - intent.putExtra(Intent.ACTION_PACKAGE_FULLY_REMOVED, packageFullyRemoved); intent.setPackage(BuildConfig.MANAGER_INJECTED_PKG_NAME); try { ActivityManagerService.broadcastIntentWithFeature(null, intent, diff --git a/core/src/main/java/org/lsposed/lspd/service/LSPosedService.java b/core/src/main/java/org/lsposed/lspd/service/LSPosedService.java index db85cdb4..fc043b28 100644 --- a/core/src/main/java/org/lsposed/lspd/service/LSPosedService.java +++ b/core/src/main/java/org/lsposed/lspd/service/LSPosedService.java @@ -42,6 +42,10 @@ import java.util.Arrays; public class LSPosedService extends ILSPosedService.Stub { private static final int AID_NOBODY = 9999; private static final int USER_NULL = -10000; + private static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED"; + public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED"; + private static final String EXTRA_USER_HANDLE = "android.intent.extra.user_handle"; + private static final String EXTRA_REMOVED_FOR_ALL_USERS = "android.intent.extra.REMOVED_FOR_ALL_USERS"; @Override public ILSPApplicationService requestApplicationService(int uid, int pid, String processName, IBinder heartBeat) { @@ -79,7 +83,7 @@ public class LSPosedService extends ILSPosedService.Stub { if (uid == AID_NOBODY || uid <= 0) return; int userId = intent.getIntExtra("android.intent.extra.user_handle", USER_NULL); var intentAction = intent.getAction(); - var allUsers = intent.getBooleanExtra("android.intent.extra.REMOVED_FOR_ALL_USERS", false); + var allUsers = intent.getBooleanExtra(EXTRA_REMOVED_FOR_ALL_USERS, false); if (userId == USER_NULL) userId = uid % PER_USER_RANGE; Uri uri = intent.getData(); String moduleName = (uri != null) ? uri.getSchemeSpecificPart() : ConfigManager.getInstance().getModule(uid); @@ -102,8 +106,8 @@ public class LSPosedService extends ILSPosedService.Stub { // because we only care about when the apk is gone if (moduleName != null && allUsers) if (ConfigManager.getInstance().removeModule(moduleName)) { - broadcastOrShowNotification(moduleName, userId, intent); isXposedModule = true; + broadcastAndShowNotification(moduleName, userId, intent, true); } break; } @@ -115,8 +119,8 @@ public class LSPosedService extends ILSPosedService.Stub { if (components != null && !Arrays.stream(components).reduce(false, (p, c) -> p || c.equals(moduleName), Boolean::logicalOr)) { return; } + broadcastAndShowNotification(moduleName, userId, intent, isXposedModule); if (isXposedModule) { - broadcastOrShowNotification(moduleName, userId, intent); // When installing a new Xposed module, we update the apk path to mark it as a // module to send a broadcast when modules that have not been activated are // uninstalled. @@ -132,8 +136,8 @@ public class LSPosedService extends ILSPosedService.Stub { case Intent.ACTION_UID_REMOVED: { // when a package is removed (rather than hide) for a single user // (apk may still be there because of multi-user) + broadcastAndShowNotification(moduleName, userId, intent, isXposedModule); if (isXposedModule) { - broadcastOrShowNotification(moduleName, userId, intent); // it will automatically remove obsolete scope from database ConfigManager.getInstance().updateCache(); } else if (ConfigManager.getInstance().isUidHooked(uid)) { @@ -159,18 +163,34 @@ public class LSPosedService extends ILSPosedService.Stub { } } - private void broadcastOrShowNotification(String moduleName, int userId, Intent intent) { - Log.d(TAG, "module " + moduleName + " changed, dispatching to manager"); - var internAction = intent.getAction(); - var allUsers = intent.getBooleanExtra("android.intent.extra.REMOVED_FOR_ALL_USERS", false); - LSPManagerService.broadcastIntent(moduleName, userId, allUsers); - var enabledModules = ConfigManager.getInstance().enabledModules(); - var scope = ConfigManager.getInstance().getModuleScope(moduleName); - boolean systemModule = scope != null && - scope.parallelStream().anyMatch(app -> app.packageName.equals("android")); - boolean enabled = Arrays.asList(enabledModules).contains(moduleName); - if (!(Intent.ACTION_UID_REMOVED.equals(internAction) || Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(internAction) || allUsers)) - LSPManagerService.showNotification(moduleName, userId, enabled, systemModule); + private void broadcastAndShowNotification(String packageName, int userId, Intent intent, boolean isXposedModule) { + Log.d(TAG, "package " + packageName + " changed, dispatching to manager"); + var action = intent.getAction(); + var allUsers = intent.getBooleanExtra(EXTRA_REMOVED_FOR_ALL_USERS, false); + intent.putExtra("android.intent.extra.PACKAGES", packageName); + intent.putExtra(Intent.EXTRA_USER, userId); + intent.putExtra("isXposedModule", isXposedModule); + LSPManagerService.broadcastIntent(intent); + if (isXposedModule) { + var enabledModules = ConfigManager.getInstance().enabledModules(); + var scope = ConfigManager.getInstance().getModuleScope(packageName); + boolean systemModule = scope != null && + scope.parallelStream().anyMatch(app -> app.packageName.equals("android")); + boolean enabled = Arrays.asList(enabledModules).contains(packageName); + if (!(Intent.ACTION_UID_REMOVED.equals(action) || Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action) || allUsers)) + LSPManagerService.showNotification(packageName, userId, enabled, systemModule); + } + } + + synchronized public void dispatchUserChanged(Intent intent) { + if (intent == null) return; + int uid = intent.getIntExtra(EXTRA_USER_HANDLE, AID_NOBODY); + if (uid == AID_NOBODY || uid <= 0) return; + try { + LSPManagerService.broadcastIntent(intent); + } catch (Throwable e) { + Log.e(TAG, "dispatch user info changed", e); + } } synchronized public void dispatchUserUnlocked(Intent intent) { @@ -335,6 +355,31 @@ public class LSPosedService extends ILSPosedService.Stub { Log.d(TAG, "registered boot receiver"); } + private void registerUserChangeReceiver() { + try { + IntentFilter userFilter = new IntentFilter(); + userFilter.addAction(ACTION_USER_ADDED); + userFilter.addAction(ACTION_USER_REMOVED); + + var receiver = new IIntentReceiver.Stub() { + @Override + public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) { + getExecutorService().submit(() -> dispatchUserChanged(intent)); + try { + ActivityManagerService.finishReceiver(this, resultCode, data, extras, false, intent.getFlags()); + } catch (Throwable e) { + Log.e(TAG, "finish receiver", e); + } + } + }; + + ActivityManagerService.registerReceiver("android", null, receiver, userFilter, null, -1, 0); + } catch (Throwable e) { + Log.e(TAG, "register user info change receiver", e); + } + Log.d(TAG, "registered user info change receiver"); + } + @Override public void dispatchSystemServerContext(IBinder activityThread, IBinder activityToken, String api) { Log.d(TAG, "received system context"); @@ -345,6 +390,7 @@ public class LSPosedService extends ILSPosedService.Stub { registerConfigurationReceiver(); registerSecretCodeReceiver(); registerBootCompleteReceiver(); + registerUserChangeReceiver(); } @Override