From dbc0226d6695a7ca95ccf7f2ccebb5c994d39bde Mon Sep 17 00:00:00 2001 From: LoveSy Date: Fri, 14 May 2021 16:28:12 +0800 Subject: [PATCH] [core] Recognize module from other user profile (#555) Co-authored-by: tehcneko <7764726+tehcneko@users.noreply.github.com> --- .../org/lsposed/manager/ConfigManager.java | 13 +- .../lsposed/manager/adapters/AppHelper.java | 3 +- .../manager/adapters/ScopeAdapter.java | 13 +- .../LSPosedManagerServiceClient.java | 5 + .../manager/receivers/ServiceReceiver.java | 7 +- .../manager/ui/activity/AppListActivity.java | 3 +- .../manager/ui/activity/ModulesActivity.java | 168 +++++++++++++++--- .../org/lsposed/manager/util/BackupUtils.java | 3 +- .../org/lsposed/manager/util/ModuleUtil.java | 38 ++-- .../manager/util/NotificationUtil.java | 3 +- app/src/main/res/layout/activity_modules.xml | 6 + app/src/main/res/values/strings.xml | 1 + .../lsposed/lspd/service/ConfigManager.java | 8 +- .../lspd/service/LSPManagerService.java | 5 + .../lsposed/lspd/service/LSPosedService.java | 4 +- .../lsposed/lspd/service/PackageService.java | 10 ++ .../org/lsposed/lspd/ILSPManagerService.aidl | 2 + 17 files changed, 232 insertions(+), 60 deletions(-) create mode 100644 app/src/main/res/layout/activity_modules.xml diff --git a/app/src/main/java/org/lsposed/manager/ConfigManager.java b/app/src/main/java/org/lsposed/manager/ConfigManager.java index 49f81cd7..9b378ac6 100644 --- a/app/src/main/java/org/lsposed/manager/ConfigManager.java +++ b/app/src/main/java/org/lsposed/manager/ConfigManager.java @@ -183,9 +183,9 @@ public class ConfigManager { } } - public static PackageInfo getPackageInfo(String packageName, int flags) throws PackageManager.NameNotFoundException { + public static PackageInfo getPackageInfo(String packageName, int flags, int userId) throws PackageManager.NameNotFoundException { try { - return LSPosedManagerServiceClient.getPackageInfo(packageName, flags, 0); + return LSPosedManagerServiceClient.getPackageInfo(packageName, flags, userId); } catch (RemoteException | NullPointerException e) { Log.e(App.TAG, Log.getStackTraceString(e)); throw new PackageManager.NameNotFoundException(); @@ -230,6 +230,15 @@ public class ConfigManager { } } + public static int[] getUsers() { + try { + return LSPosedManagerServiceClient.getUsers(); + } catch (RemoteException | NullPointerException e) { + Log.e(App.TAG, Log.getStackTraceString(e)); + return null; + } + } + public static boolean isMagiskInstalled() { return Arrays.stream(System.getenv("PATH").split(File.pathSeparator)) .anyMatch(str -> new File(str, "magisk").exists()); 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 42f6144f..907f5db1 100644 --- a/app/src/main/java/org/lsposed/manager/adapters/AppHelper.java +++ b/app/src/main/java/org/lsposed/manager/adapters/AppHelper.java @@ -40,12 +40,13 @@ public class AppHelper { public static final String SETTINGS_CATEGORY = "de.robv.android.xposed.category.MODULE_SETTINGS"; private static List appList; - public static Intent getSettingsIntent(String packageName, PackageManager packageManager) { + public static Intent getSettingsIntent(String packageName, int userId, PackageManager packageManager) { // taken from // ApplicationPackageManager.getLaunchIntentForPackage(String) // first looks for an Xposed-specific category, falls back to // getLaunchIntentForPackage + //TODO:multiuser Intent intentToResolve = new Intent(Intent.ACTION_MAIN); intentToResolve.addCategory(SETTINGS_CATEGORY); intentToResolve.setPackage(packageName); 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 2c2662fd..18cd6746 100644 --- a/app/src/main/java/org/lsposed/manager/adapters/ScopeAdapter.java +++ b/app/src/main/java/org/lsposed/manager/adapters/ScopeAdapter.java @@ -151,6 +151,9 @@ public class ScopeAdapter extends RecyclerView.Adapter } private boolean shouldHideApp(PackageInfo info, ApplicationWithEquals app) { + if (app.userId != module.userId) { + return true; + } if (info.packageName.equals(this.module.packageName)) { return true; } @@ -251,7 +254,7 @@ public class ScopeAdapter extends RecyclerView.Adapter item.setChecked(!item.isChecked()); preferences.edit().putBoolean("filter_modules", item.isChecked()).apply(); } else if (itemId == R.id.menu_launch) { - Intent launchIntent = AppHelper.getSettingsIntent(module.packageName, pm); + Intent launchIntent = AppHelper.getSettingsIntent(module.packageName, module.userId, pm); if (launchIntent != null) { activity.startActivity(launchIntent); } else { @@ -320,7 +323,7 @@ public class ScopeAdapter extends RecyclerView.Adapter public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { inflater.inflate(R.menu.menu_app_list, menu); - Intent intent = AppHelper.getSettingsIntent(module.packageName, pm); + Intent intent = AppHelper.getSettingsIntent(module.packageName, module.userId, pm); if (intent == null) { menu.removeItem(R.id.menu_launch); } @@ -366,11 +369,7 @@ public class ScopeAdapter extends RecyclerView.Adapter boolean android = appInfo.packageName.equals("android"); CharSequence appName; int userId = appInfo.applicationInfo.uid / 100000; - if (userId != 0) { - appName = String.format("%s (%s)", appInfo.label, userId); - } else { - appName = android ? activity.getString(R.string.android_framework) : appInfo.label; - } + appName = android ? activity.getString(R.string.android_framework) : appInfo.label; holder.appName.setText(appName); GlideApp.with(holder.appIcon) .load(appInfo.packageInfo) diff --git a/app/src/main/java/org/lsposed/manager/receivers/LSPosedManagerServiceClient.java b/app/src/main/java/org/lsposed/manager/receivers/LSPosedManagerServiceClient.java index 1dafadb3..692b4d53 100644 --- a/app/src/main/java/org/lsposed/manager/receivers/LSPosedManagerServiceClient.java +++ b/app/src/main/java/org/lsposed/manager/receivers/LSPosedManagerServiceClient.java @@ -153,4 +153,9 @@ public class LSPosedManagerServiceClient { ensureService(); return service.isSepolicyLoaded(); } + + public static int[] getUsers() throws RemoteException, NullPointerException { + ensureService(); + return service.getUsers(); + } } diff --git a/app/src/main/java/org/lsposed/manager/receivers/ServiceReceiver.java b/app/src/main/java/org/lsposed/manager/receivers/ServiceReceiver.java index fc46fc01..65a9c716 100644 --- a/app/src/main/java/org/lsposed/manager/receivers/ServiceReceiver.java +++ b/app/src/main/java/org/lsposed/manager/receivers/ServiceReceiver.java @@ -37,20 +37,21 @@ public class ServiceReceiver extends BroadcastReceiver { @Override public void onReceive(final Context context, final Intent intent) { + int userId = intent.getIntExtra(Intent.EXTRA_USER, 0); String packageName = getPackageName(intent); if (packageName == null) { return; } - ModuleUtil.InstalledModule module = ModuleUtil.getInstance().reloadSingleModule(packageName); + ModuleUtil.InstalledModule module = ModuleUtil.getInstance().reloadSingleModule(packageName, userId); if (module == null) { return; } if (intent.getAction().equals("org.lsposed.action.MODULE_NOT_ACTIVATAED")) { - NotificationUtil.showNotification(context, packageName, module.getAppName(), false); + NotificationUtil.showNotification(context, packageName, module.getAppName(), userId, false); } else if (intent.getAction().equals("org.lsposed.action.MODULE_UPDATED")) { - NotificationUtil.showNotification(context, packageName, module.getAppName(), true); + NotificationUtil.showNotification(context, packageName, module.getAppName(), userId, true); } } } diff --git a/app/src/main/java/org/lsposed/manager/ui/activity/AppListActivity.java b/app/src/main/java/org/lsposed/manager/ui/activity/AppListActivity.java index da0968ab..c330fe18 100644 --- a/app/src/main/java/org/lsposed/manager/ui/activity/AppListActivity.java +++ b/app/src/main/java/org/lsposed/manager/ui/activity/AppListActivity.java @@ -61,12 +61,13 @@ public class AppListActivity extends BaseActivity { public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); String modulePackageName = getIntent().getStringExtra("modulePackageName"); + int moduleUserId = getIntent().getIntExtra("moduleUserId", -1); binding = ActivityAppListBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); setAppBar(binding.appBar, binding.toolbar); binding.appBar.setRaised(true); binding.toolbar.setNavigationOnClickListener(view -> onBackPressed()); - ModuleUtil.InstalledModule module = ModuleUtil.getInstance().getModule(modulePackageName); + ModuleUtil.InstalledModule module = ModuleUtil.getInstance().getModule(modulePackageName, moduleUserId); if (module == null) { finish(); return; diff --git a/app/src/main/java/org/lsposed/manager/ui/activity/ModulesActivity.java b/app/src/main/java/org/lsposed/manager/ui/activity/ModulesActivity.java index 07cdbc58..ae5ea515 100644 --- a/app/src/main/java/org/lsposed/manager/ui/activity/ModulesActivity.java +++ b/app/src/main/java/org/lsposed/manager/ui/activity/ModulesActivity.java @@ -45,40 +45,57 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.Filter; +import android.widget.Filterable; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.widget.SearchView; import androidx.core.content.ContextCompat; import androidx.lifecycle.Lifecycle; import androidx.recyclerview.widget.RecyclerView; +import androidx.viewpager2.widget.ViewPager2; import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.transition.Transition; import com.google.android.material.snackbar.Snackbar; +import com.google.android.material.tabs.TabLayoutMediator; import org.lsposed.manager.ConfigManager; import org.lsposed.manager.R; import org.lsposed.manager.adapters.AppHelper; +import org.lsposed.manager.databinding.ActivityModuleDetailBinding; +import org.lsposed.manager.databinding.ItemRepoRecyclerviewBinding; import org.lsposed.manager.repo.RepoLoader; -import org.lsposed.manager.ui.activity.base.ListActivity; +import org.lsposed.manager.ui.activity.base.BaseActivity; import org.lsposed.manager.util.GlideApp; +import org.lsposed.manager.util.LinearLayoutManagerFix; import org.lsposed.manager.util.ModuleUtil; import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.stream.Collectors; -public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleListener { +import rikka.recyclerview.RecyclerViewKt; +import rikka.widget.borderview.BorderRecyclerView; +import rikka.widget.borderview.BorderView; + +public class ModulesActivity extends BaseActivity implements ModuleUtil.ModuleListener { + + protected ActivityModuleDetailBinding binding; + protected SearchView searchView; + private SearchView.OnQueryTextListener mSearchListener; + private final ArrayList adapters = new ArrayList<>(); private static final Handler uninstallHandler; private PackageManager pm; private ModuleUtil moduleUtil; - private ModuleAdapter adapter = null; - private String selectedPackageName; + private ModuleUtil.InstalledModule selectedModule; static { HandlerThread uninstallThread = new HandlerThread("uninstall"); @@ -88,20 +105,89 @@ public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleLi @Override public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + binding = ActivityModuleDetailBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + setAppBar(binding.appBar, binding.toolbar); + binding.getRoot().bringChildToFront(binding.appBar); + binding.toolbar.setNavigationOnClickListener(view -> onBackPressed()); + ActionBar bar = getSupportActionBar(); + if (bar != null) { + bar.setDisplayHomeAsUpEnabled(true); + } + binding.viewPager.setAdapter(new PagerAdapter()); + binding.viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { + @Override + public void onPageSelected(int position) { + BorderRecyclerView recyclerView = findViewById(R.id.recyclerView); + + if (recyclerView != null) { + binding.appBar.setRaised(!recyclerView.getBorderViewDelegate().isShowingTopBorder()); + + } + } + }); + mSearchListener = new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String query) { + adapters.forEach(adapter -> { + adapter.getFilter().filter(query); + }); + return false; + } + + @Override + public boolean onQueryTextChange(String newText) { + adapters.forEach(adapter -> { + adapter.getFilter().filter(newText); + }); + return false; + } + }; moduleUtil = ModuleUtil.getInstance(); pm = getPackageManager(); moduleUtil.addListener(this); - super.onCreate(savedInstanceState); + int[] users = ConfigManager.getUsers(); + if (users != null) { + if (users.length != 1) { + ArrayList titles = new ArrayList<>(); + for (int userId : users) { + var adapter = new ModuleAdapter(userId); + adapter.setHasStableIds(true); + adapters.add(adapter); + titles.add(getString(R.string.user_title, userId)); + } + new TabLayoutMediator(binding.tabLayout, binding.viewPager, (tab, position) -> tab.setText(titles.get(position))).attach(); + } else { + binding.tabLayout.setVisibility(View.GONE); + } + } if (ConfigManager.getXposedVersionName() == null) { Toast.makeText(this, R.string.lsposed_not_active, Toast.LENGTH_LONG).show(); finish(); } } + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView(); + searchView.setOnQueryTextListener(mSearchListener); + return super.onPrepareOptionsMenu(menu); + } + + @Override + public void onBackPressed() { + if (searchView.isIconified()) { + super.onBackPressed(); + } else { + searchView.setIconified(true); + } + } + @Override protected void onResume() { super.onResume(); - adapter.refresh(true); + adapters.forEach(ModuleAdapter::refresh); } @Override @@ -117,22 +203,22 @@ public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleLi } @Override - public void onSingleInstalledModuleReloaded(ModuleUtil moduleUtil, String packageName, ModuleUtil.InstalledModule module) { - adapter.refresh(); + public void onSingleInstalledModuleReloaded() { + adapters.forEach(ModuleAdapter::refresh); } @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { int itemId = item.getItemId(); if (itemId == R.id.menu_refresh) { - adapter.refresh(true); + adapters.forEach(ModuleAdapter::refresh); } return super.onOptionsItemSelected(item); } @Override public boolean onContextItemSelected(@NonNull MenuItem item) { - ModuleUtil.InstalledModule module = ModuleUtil.getInstance().getModule(selectedPackageName); + ModuleUtil.InstalledModule module = ModuleUtil.getInstance().getModule(selectedModule.packageName, selectedModule.userId); if (module == null) { return false; } @@ -142,7 +228,7 @@ public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleLi if (packageName == null) { return false; } - Intent intent = AppHelper.getSettingsIntent(packageName, pm); + Intent intent = AppHelper.getSettingsIntent(packageName, module.userId, pm); if (intent != null) { startActivity(intent); } else { @@ -177,7 +263,8 @@ public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleLi Toast.makeText(ModulesActivity.this, text, Toast.LENGTH_SHORT).show(); } }); - if (success) moduleUtil.reloadSingleModule(module.packageName); + if (success) + moduleUtil.reloadSingleModule(module.packageName, module.userId); })) .setNegativeButton(android.R.string.cancel, null) .show(); @@ -192,16 +279,45 @@ public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleLi return super.onContextItemSelected(item); } - @Override - protected BaseAdapter createAdapter() { - return adapter = new ModuleAdapter(); + private class PagerAdapter extends RecyclerView.Adapter { + + @NonNull + @Override + public PagerAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new PagerAdapter.ViewHolder(ItemRepoRecyclerviewBinding.inflate(getLayoutInflater(), parent, false).getRoot()); + } + + @Override + public void onBindViewHolder(@NonNull PagerAdapter.ViewHolder holder, int position) { + holder.recyclerView.setAdapter(adapters.get(position)); + holder.recyclerView.setLayoutManager(new LinearLayoutManagerFix(ModulesActivity.this)); + holder.recyclerView.getBorderViewDelegate().setBorderVisibilityChangedListener((top, oldTop, bottom, oldBottom) -> binding.appBar.setRaised(!top)); + RecyclerViewKt.fixEdgeEffect(holder.recyclerView, false, true); + RecyclerViewKt.addFastScroller(holder.recyclerView, holder.itemView); + } + + @Override + public int getItemCount() { + return adapters.size(); + } + + class ViewHolder extends RecyclerView.ViewHolder { + BorderRecyclerView recyclerView; + + public ViewHolder(@NonNull View itemView) { + super(itemView); + recyclerView = itemView.findViewById(R.id.recyclerView); + } + } } - private class ModuleAdapter extends BaseAdapter { + private class ModuleAdapter extends RecyclerView.Adapter implements Filterable { private final List searchList = new ArrayList<>(); private final List showList = new ArrayList<>(); + private final int userId; - ModuleAdapter() { + ModuleAdapter(int userId) { + this.userId = userId; refresh(); } @@ -216,7 +332,13 @@ public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleLi public void onBindViewHolder(@NonNull ViewHolder holder, int position) { ModuleUtil.InstalledModule item = showList.get(position); holder.root.setAlpha(moduleUtil.isModuleEnabled(item.packageName) ? 1.0f : .5f); - holder.appName.setText(item.getAppName()); + String appName; + if (item.userId != 0) { + appName = String.format("%s (%s)", item.getAppName(), item.userId); + } else { + appName = item.getAppName(); + } + holder.appName.setText(appName); GlideApp.with(holder.appIcon) .load(item.getPackageInfo()) .into(new CustomTarget() { @@ -266,7 +388,7 @@ public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleLi holder.itemView.setOnCreateContextMenuListener((menu, v, menuInfo) -> { getMenuInflater().inflate(R.menu.context_menu_modules, menu); menu.setHeaderTitle(item.getAppName()); - Intent intent = AppHelper.getSettingsIntent(item.packageName, pm); + Intent intent = AppHelper.getSettingsIntent(item.packageName, item.userId, pm); if (intent == null) { menu.removeItem(R.id.menu_launch); } @@ -278,11 +400,12 @@ public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleLi holder.itemView.setOnClickListener(v -> { Intent intent = new Intent(ModulesActivity.this, AppListActivity.class); intent.putExtra("modulePackageName", item.packageName); + intent.putExtra("moduleUserId", item.userId); startActivity(intent); }); holder.itemView.setOnLongClickListener(v -> { - selectedPackageName = item.packageName; + selectedModule = item; return false; }); @@ -298,7 +421,8 @@ public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleLi @Override public long getItemId(int position) { - return showList.get(position).packageName.hashCode(); + var module = showList.get(position); + return (module.packageName + "!" + module.userId).hashCode(); } @Override @@ -318,7 +442,7 @@ public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleLi private final Runnable reloadModules = new Runnable() { public void run() { searchList.clear(); - searchList.addAll(moduleUtil.getModules().values()); + searchList.addAll(moduleUtil.getModules().values().stream().filter(module -> module.userId == userId).collect(Collectors.toList())); Comparator cmp = AppHelper.getAppListComparator(0, pm); searchList.sort((a, b) -> { boolean aChecked = moduleUtil.isModuleEnabled(a.packageName); diff --git a/app/src/main/java/org/lsposed/manager/util/BackupUtils.java b/app/src/main/java/org/lsposed/manager/util/BackupUtils.java index e2be5fea..99d980a3 100644 --- a/app/src/main/java/org/lsposed/manager/util/BackupUtils.java +++ b/app/src/main/java/org/lsposed/manager/util/BackupUtils.java @@ -32,7 +32,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; @@ -48,7 +47,7 @@ public class BackupUtils { JSONObject rootObject = new JSONObject(); rootObject.put("version", VERSION); JSONArray modulesArray = new JSONArray(); - Map modules = ModuleUtil.getInstance().getModules(); + var modules = ModuleUtil.getInstance().getModules(); for (ModuleUtil.InstalledModule module : modules.values()) { if (packageName != null && !module.packageName.equals(packageName)) { continue; 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 f5660af1..fb4c9f96 100644 --- a/app/src/main/java/org/lsposed/manager/util/ModuleUtil.java +++ b/app/src/main/java/org/lsposed/manager/util/ModuleUtil.java @@ -27,6 +27,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.os.Build; import androidx.annotation.NonNull; +import androidx.core.util.Pair; import org.lsposed.manager.App; import org.lsposed.manager.ConfigManager; @@ -47,7 +48,7 @@ public final class ModuleUtil { private final PackageManager pm; private final List listeners = new CopyOnWriteArrayList<>(); private final HashSet enabledModules; - private Map installedModules; + private Map, InstalledModule> installedModules; private boolean isReloading = false; private ModuleUtil() { @@ -82,15 +83,15 @@ public final class ModuleUtil { isReloading = true; } - Map modules = new HashMap<>(); + Map, InstalledModule> modules = new HashMap<>(); for (PackageInfo pkg : ConfigManager.getInstalledPackagesFromAllUsers(PackageManager.GET_META_DATA, false)) { ApplicationInfo app = pkg.applicationInfo; - if (!app.enabled || app.uid / 100000 != 0) + if (!app.enabled) continue; if (app.metaData != null && app.metaData.containsKey("xposedminversion")) { InstalledModule installed = new InstalledModule(pkg, false); - modules.put(pkg.packageName, installed); + modules.put(Pair.create(pkg.packageName, app.uid / 100000), installed); } } @@ -101,10 +102,10 @@ public final class ModuleUtil { } } - public InstalledModule reloadSingleModule(String packageName) { + public InstalledModule reloadSingleModule(String packageName, int userId) { PackageInfo pkg; try { - pkg = ConfigManager.getPackageInfo(packageName, PackageManager.GET_META_DATA); + pkg = ConfigManager.getPackageInfo(packageName, PackageManager.GET_META_DATA, userId); if (pkg == null) { throw new NameNotFoundException(); } @@ -112,7 +113,7 @@ public final class ModuleUtil { InstalledModule old = installedModules.remove(packageName); if (old != null) { for (ModuleListener listener : listeners) { - listener.onSingleInstalledModuleReloaded(instance, packageName, null); + listener.onSingleInstalledModuleReloaded(); } } return null; @@ -121,28 +122,31 @@ public final class ModuleUtil { ApplicationInfo app = pkg.applicationInfo; if (app.enabled && app.metaData != null && app.metaData.containsKey("xposedminversion")) { InstalledModule module = new InstalledModule(pkg, false); - installedModules.put(packageName, module); + installedModules.put(Pair.create(packageName, userId), module); for (ModuleListener listener : listeners) { - listener.onSingleInstalledModuleReloaded(instance, packageName, - module); + listener.onSingleInstalledModuleReloaded(); } return module; } else { - InstalledModule old = installedModules.remove(packageName); + InstalledModule old = installedModules.remove(Pair.create(packageName, userId)); if (old != null) { for (ModuleListener listener : listeners) { - listener.onSingleInstalledModuleReloaded(instance, packageName, null); + listener.onSingleInstalledModuleReloaded(); } } return null; } } - public InstalledModule getModule(String packageName) { - return installedModules.get(packageName); + public InstalledModule getModule(String packageName, int userId) { + return installedModules.get(Pair.create(packageName, userId)); } - public Map getModules() { + public InstalledModule getModule(String packageName) { + return getModule(packageName, 0); + } + + public Map, InstalledModule> getModules() { return installedModules; } @@ -180,11 +184,12 @@ public final class ModuleUtil { * Called whenever one (previously or now) installed module has been * reloaded */ - void onSingleInstalledModuleReloaded(ModuleUtil moduleUtil, String packageName, InstalledModule module); + void onSingleInstalledModuleReloaded(); } public class InstalledModule { //private static final int FLAG_FORWARD_LOCK = 1 << 29; + public final int userId; public final String packageName; public final String versionName; public final long versionCode; @@ -201,6 +206,7 @@ public final class ModuleUtil { private InstalledModule(PackageInfo pkg, boolean isFramework) { this.app = pkg.applicationInfo; this.pkg = pkg; + this.userId = pkg.applicationInfo.uid / 100000; this.packageName = pkg.packageName; this.isFramework = isFramework; this.versionName = pkg.versionName; diff --git a/app/src/main/java/org/lsposed/manager/util/NotificationUtil.java b/app/src/main/java/org/lsposed/manager/util/NotificationUtil.java index bcf868be..3850fffe 100644 --- a/app/src/main/java/org/lsposed/manager/util/NotificationUtil.java +++ b/app/src/main/java/org/lsposed/manager/util/NotificationUtil.java @@ -38,7 +38,7 @@ public final class NotificationUtil { private static final int PENDING_INTENT_OPEN_APP_LIST = 0; private static final String NOTIFICATION_MODULES_CHANNEL = "modules_channel_2"; - public static void showNotification(Context context, String modulePackageName, String moduleName, boolean enabled) { + public static void showNotification(Context context, String modulePackageName, String moduleName, int moduleUserId, boolean enabled) { NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); NotificationChannel channel = new NotificationChannel(NOTIFICATION_MODULES_CHANNEL, @@ -53,6 +53,7 @@ public final class NotificationUtil { Intent intent = new Intent(context, AppListActivity.class) .putExtra("modulePackageName", modulePackageName) + .putExtra("moduleUserId", moduleUserId) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); PendingIntent contentIntent = PendingIntent.getActivity(context, PENDING_INTENT_OPEN_APP_LIST, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); diff --git a/app/src/main/res/layout/activity_modules.xml b/app/src/main/res/layout/activity_modules.xml new file mode 100644 index 00000000..13544084 --- /dev/null +++ b/app/src/main/res/layout/activity_modules.xml @@ -0,0 +1,6 @@ + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 20bb6ede..6d56117c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -85,6 +85,7 @@ Do you want to uninstall this module? Uninstalled %1$s Uninstall unsuccessful + User %d Re-optimize diff --git a/core/src/main/java/org/lsposed/lspd/service/ConfigManager.java b/core/src/main/java/org/lsposed/lspd/service/ConfigManager.java index c569bbcd..09068972 100644 --- a/core/src/main/java/org/lsposed/lspd/service/ConfigManager.java +++ b/core/src/main/java/org/lsposed/lspd/service/ConfigManager.java @@ -68,6 +68,8 @@ public class ConfigManager { "android.permission.WRITE_SECURE_SETTINGS" }; + private static final int PER_USER_RANGE = 100000; + static ConfigManager instance = null; private static final File basePath = new File("/data/adb/lspd"); @@ -312,9 +314,9 @@ public class ConfigManager { while (cursor.moveToNext()) { String packageName = cursor.getString(pkgNameIdx); try { - PackageInfo pkgInfo = PackageService.getPackageInfo(packageName, 0, 0); + PackageInfo pkgInfo = PackageService.getPackageInfoFromAllUsers(packageName, 0); if (pkgInfo != null && pkgInfo.applicationInfo != null) { - cachedModule.put(pkgInfo.applicationInfo.uid, pkgInfo.packageName); + cachedModule.put(pkgInfo.applicationInfo.uid % PER_USER_RANGE, pkgInfo.packageName); } else { obsoleteModules.add(packageName); } @@ -635,7 +637,7 @@ public class ConfigManager { } public boolean isModule(int uid) { - return cachedModule.containsKey(uid); + return cachedModule.containsKey(uid % PER_USER_RANGE); } private void recursivelyChown(File file, int uid, int gid) throws ErrnoException { 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 3b36219e..ca08ca87 100644 --- a/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java +++ b/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java @@ -161,4 +161,9 @@ public class LSPManagerService extends ILSPManagerService.Stub { public boolean isSepolicyLoaded() throws RemoteException { return ConfigManager.getInstance().isSepolicyLoaded(); } + + @Override + public int[] getUsers() throws RemoteException { + return UserService.getUsers(); + } } 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 243f802e..c6c79b8e 100644 --- a/core/src/main/java/org/lsposed/lspd/service/LSPosedService.java +++ b/core/src/main/java/org/lsposed/lspd/service/LSPosedService.java @@ -102,8 +102,7 @@ public class LSPosedService extends ILSPosedService.Stub { } ApplicationInfo applicationInfo = PackageService.getApplicationInfo(packageName, PackageManager.GET_META_DATA, 0); - boolean isXposedModule = (userId == 0 || userId == -1) && - applicationInfo != null && + boolean isXposedModule = applicationInfo != null && applicationInfo.enabled && applicationInfo.metaData != null && applicationInfo.metaData.containsKey("xposedminversion"); @@ -118,6 +117,7 @@ public class LSPosedService extends ILSPosedService.Stub { broadcastIntent.addFlags(0x01000000); broadcastIntent.addFlags(0x00400000); broadcastIntent.setData(intent.getData()); + broadcastIntent.putExtras(intent.getExtras()); broadcastIntent.setComponent(ComponentName.unflattenFromString(ConfigManager.getInstance().getManagerPackageName() + "/.receivers.ServiceReceiver")); try { diff --git a/core/src/main/java/org/lsposed/lspd/service/PackageService.java b/core/src/main/java/org/lsposed/lspd/service/PackageService.java index ccbfac9b..4d1691af 100644 --- a/core/src/main/java/org/lsposed/lspd/service/PackageService.java +++ b/core/src/main/java/org/lsposed/lspd/service/PackageService.java @@ -96,6 +96,16 @@ public class PackageService { return pm.getPackageInfo(packageName, flags, userId); } + public static PackageInfo getPackageInfoFromAllUsers(String packageName, int flags) throws RemoteException { + IPackageManager pm = getPackageManager(); + if (pm == null) return null; + for (int userId : UserService.getUsers()) { + var info = pm.getPackageInfo(packageName, flags, userId); + if (info != null) return info; + } + return null; + } + public static ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) throws RemoteException { IPackageManager pm = getPackageManager(); if (pm == null) return null; diff --git a/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl b/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl index e4e9b27a..0876d191 100644 --- a/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl +++ b/manager-service/src/main/aidl/org/lsposed/lspd/ILSPManagerService.aidl @@ -46,4 +46,6 @@ interface ILSPManagerService { boolean uninstallPackage(String packageName) = 25; boolean isSepolicyLoaded() = 26; + + int[] getUsers() = 27; }