[core] Recognize module from other user profile (#555)

Co-authored-by: tehcneko <7764726+tehcneko@users.noreply.github.com>
This commit is contained in:
LoveSy 2021-05-14 16:28:12 +08:00 committed by GitHub
parent b0f3c4d0bb
commit dbc0226d66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 232 additions and 60 deletions

View File

@ -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 { try {
return LSPosedManagerServiceClient.getPackageInfo(packageName, flags, 0); return LSPosedManagerServiceClient.getPackageInfo(packageName, flags, userId);
} catch (RemoteException | NullPointerException e) { } catch (RemoteException | NullPointerException e) {
Log.e(App.TAG, Log.getStackTraceString(e)); Log.e(App.TAG, Log.getStackTraceString(e));
throw new PackageManager.NameNotFoundException(); 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() { public static boolean isMagiskInstalled() {
return Arrays.stream(System.getenv("PATH").split(File.pathSeparator)) return Arrays.stream(System.getenv("PATH").split(File.pathSeparator))
.anyMatch(str -> new File(str, "magisk").exists()); .anyMatch(str -> new File(str, "magisk").exists());

View File

@ -40,12 +40,13 @@ public class AppHelper {
public static final String SETTINGS_CATEGORY = "de.robv.android.xposed.category.MODULE_SETTINGS"; public static final String SETTINGS_CATEGORY = "de.robv.android.xposed.category.MODULE_SETTINGS";
private static List<PackageInfo> appList; private static List<PackageInfo> appList;
public static Intent getSettingsIntent(String packageName, PackageManager packageManager) { public static Intent getSettingsIntent(String packageName, int userId, PackageManager packageManager) {
// taken from // taken from
// ApplicationPackageManager.getLaunchIntentForPackage(String) // ApplicationPackageManager.getLaunchIntentForPackage(String)
// first looks for an Xposed-specific category, falls back to // first looks for an Xposed-specific category, falls back to
// getLaunchIntentForPackage // getLaunchIntentForPackage
//TODO:multiuser
Intent intentToResolve = new Intent(Intent.ACTION_MAIN); Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
intentToResolve.addCategory(SETTINGS_CATEGORY); intentToResolve.addCategory(SETTINGS_CATEGORY);
intentToResolve.setPackage(packageName); intentToResolve.setPackage(packageName);

View File

@ -151,6 +151,9 @@ public class ScopeAdapter extends RecyclerView.Adapter<ScopeAdapter.ViewHolder>
} }
private boolean shouldHideApp(PackageInfo info, ApplicationWithEquals app) { private boolean shouldHideApp(PackageInfo info, ApplicationWithEquals app) {
if (app.userId != module.userId) {
return true;
}
if (info.packageName.equals(this.module.packageName)) { if (info.packageName.equals(this.module.packageName)) {
return true; return true;
} }
@ -251,7 +254,7 @@ public class ScopeAdapter extends RecyclerView.Adapter<ScopeAdapter.ViewHolder>
item.setChecked(!item.isChecked()); item.setChecked(!item.isChecked());
preferences.edit().putBoolean("filter_modules", item.isChecked()).apply(); preferences.edit().putBoolean("filter_modules", item.isChecked()).apply();
} else if (itemId == R.id.menu_launch) { } 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) { if (launchIntent != null) {
activity.startActivity(launchIntent); activity.startActivity(launchIntent);
} else { } else {
@ -320,7 +323,7 @@ public class ScopeAdapter extends RecyclerView.Adapter<ScopeAdapter.ViewHolder>
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
inflater.inflate(R.menu.menu_app_list, menu); 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) { if (intent == null) {
menu.removeItem(R.id.menu_launch); menu.removeItem(R.id.menu_launch);
} }
@ -366,11 +369,7 @@ public class ScopeAdapter extends RecyclerView.Adapter<ScopeAdapter.ViewHolder>
boolean android = appInfo.packageName.equals("android"); boolean android = appInfo.packageName.equals("android");
CharSequence appName; CharSequence appName;
int userId = appInfo.applicationInfo.uid / 100000; int userId = appInfo.applicationInfo.uid / 100000;
if (userId != 0) { appName = android ? activity.getString(R.string.android_framework) : appInfo.label;
appName = String.format("%s (%s)", appInfo.label, userId);
} else {
appName = android ? activity.getString(R.string.android_framework) : appInfo.label;
}
holder.appName.setText(appName); holder.appName.setText(appName);
GlideApp.with(holder.appIcon) GlideApp.with(holder.appIcon)
.load(appInfo.packageInfo) .load(appInfo.packageInfo)

View File

@ -153,4 +153,9 @@ public class LSPosedManagerServiceClient {
ensureService(); ensureService();
return service.isSepolicyLoaded(); return service.isSepolicyLoaded();
} }
public static int[] getUsers() throws RemoteException, NullPointerException {
ensureService();
return service.getUsers();
}
} }

View File

@ -37,20 +37,21 @@ public class ServiceReceiver extends BroadcastReceiver {
@Override @Override
public void onReceive(final Context context, final Intent intent) { public void onReceive(final Context context, final Intent intent) {
int userId = intent.getIntExtra(Intent.EXTRA_USER, 0);
String packageName = getPackageName(intent); String packageName = getPackageName(intent);
if (packageName == null) { if (packageName == null) {
return; return;
} }
ModuleUtil.InstalledModule module = ModuleUtil.getInstance().reloadSingleModule(packageName); ModuleUtil.InstalledModule module = ModuleUtil.getInstance().reloadSingleModule(packageName, userId);
if (module == null) { if (module == null) {
return; return;
} }
if (intent.getAction().equals("org.lsposed.action.MODULE_NOT_ACTIVATAED")) { 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")) { } 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);
} }
} }
} }

View File

@ -61,12 +61,13 @@ public class AppListActivity extends BaseActivity {
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
String modulePackageName = getIntent().getStringExtra("modulePackageName"); String modulePackageName = getIntent().getStringExtra("modulePackageName");
int moduleUserId = getIntent().getIntExtra("moduleUserId", -1);
binding = ActivityAppListBinding.inflate(getLayoutInflater()); binding = ActivityAppListBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setAppBar(binding.appBar, binding.toolbar); setAppBar(binding.appBar, binding.toolbar);
binding.appBar.setRaised(true); binding.appBar.setRaised(true);
binding.toolbar.setNavigationOnClickListener(view -> onBackPressed()); binding.toolbar.setNavigationOnClickListener(view -> onBackPressed());
ModuleUtil.InstalledModule module = ModuleUtil.getInstance().getModule(modulePackageName); ModuleUtil.InstalledModule module = ModuleUtil.getInstance().getModule(modulePackageName, moduleUserId);
if (module == null) { if (module == null) {
finish(); finish();
return; return;

View File

@ -45,40 +45,57 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Filter; import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SearchView;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.lifecycle.Lifecycle; import androidx.lifecycle.Lifecycle;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.ViewPager2;
import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition; import com.bumptech.glide.request.transition.Transition;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.tabs.TabLayoutMediator;
import org.lsposed.manager.ConfigManager; import org.lsposed.manager.ConfigManager;
import org.lsposed.manager.R; import org.lsposed.manager.R;
import org.lsposed.manager.adapters.AppHelper; 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.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.GlideApp;
import org.lsposed.manager.util.LinearLayoutManagerFix;
import org.lsposed.manager.util.ModuleUtil; import org.lsposed.manager.util.ModuleUtil;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; 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<ModuleAdapter> adapters = new ArrayList<>();
private static final Handler uninstallHandler; private static final Handler uninstallHandler;
private PackageManager pm; private PackageManager pm;
private ModuleUtil moduleUtil; private ModuleUtil moduleUtil;
private ModuleAdapter adapter = null; private ModuleUtil.InstalledModule selectedModule;
private String selectedPackageName;
static { static {
HandlerThread uninstallThread = new HandlerThread("uninstall"); HandlerThread uninstallThread = new HandlerThread("uninstall");
@ -88,20 +105,89 @@ public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleLi
@Override @Override
public void onCreate(Bundle savedInstanceState) { 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(); moduleUtil = ModuleUtil.getInstance();
pm = getPackageManager(); pm = getPackageManager();
moduleUtil.addListener(this); moduleUtil.addListener(this);
super.onCreate(savedInstanceState); int[] users = ConfigManager.getUsers();
if (users != null) {
if (users.length != 1) {
ArrayList<String> 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) { if (ConfigManager.getXposedVersionName() == null) {
Toast.makeText(this, R.string.lsposed_not_active, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.lsposed_not_active, Toast.LENGTH_LONG).show();
finish(); 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 @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
adapter.refresh(true); adapters.forEach(ModuleAdapter::refresh);
} }
@Override @Override
@ -117,22 +203,22 @@ public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleLi
} }
@Override @Override
public void onSingleInstalledModuleReloaded(ModuleUtil moduleUtil, String packageName, ModuleUtil.InstalledModule module) { public void onSingleInstalledModuleReloaded() {
adapter.refresh(); adapters.forEach(ModuleAdapter::refresh);
} }
@Override @Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) { public boolean onOptionsItemSelected(@NonNull MenuItem item) {
int itemId = item.getItemId(); int itemId = item.getItemId();
if (itemId == R.id.menu_refresh) { if (itemId == R.id.menu_refresh) {
adapter.refresh(true); adapters.forEach(ModuleAdapter::refresh);
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@Override @Override
public boolean onContextItemSelected(@NonNull MenuItem item) { 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) { if (module == null) {
return false; return false;
} }
@ -142,7 +228,7 @@ public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleLi
if (packageName == null) { if (packageName == null) {
return false; return false;
} }
Intent intent = AppHelper.getSettingsIntent(packageName, pm); Intent intent = AppHelper.getSettingsIntent(packageName, module.userId, pm);
if (intent != null) { if (intent != null) {
startActivity(intent); startActivity(intent);
} else { } else {
@ -177,7 +263,8 @@ public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleLi
Toast.makeText(ModulesActivity.this, text, Toast.LENGTH_SHORT).show(); 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) .setNegativeButton(android.R.string.cancel, null)
.show(); .show();
@ -192,16 +279,45 @@ public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleLi
return super.onContextItemSelected(item); return super.onContextItemSelected(item);
} }
@Override private class PagerAdapter extends RecyclerView.Adapter<PagerAdapter.ViewHolder> {
protected BaseAdapter<?> createAdapter() {
return adapter = new ModuleAdapter(); @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<ModuleAdapter.ViewHolder> { private class ModuleAdapter extends RecyclerView.Adapter<ModuleAdapter.ViewHolder> implements Filterable {
private final List<ModuleUtil.InstalledModule> searchList = new ArrayList<>(); private final List<ModuleUtil.InstalledModule> searchList = new ArrayList<>();
private final List<ModuleUtil.InstalledModule> showList = new ArrayList<>(); private final List<ModuleUtil.InstalledModule> showList = new ArrayList<>();
private final int userId;
ModuleAdapter() { ModuleAdapter(int userId) {
this.userId = userId;
refresh(); refresh();
} }
@ -216,7 +332,13 @@ public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleLi
public void onBindViewHolder(@NonNull ViewHolder holder, int position) { public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
ModuleUtil.InstalledModule item = showList.get(position); ModuleUtil.InstalledModule item = showList.get(position);
holder.root.setAlpha(moduleUtil.isModuleEnabled(item.packageName) ? 1.0f : .5f); 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) GlideApp.with(holder.appIcon)
.load(item.getPackageInfo()) .load(item.getPackageInfo())
.into(new CustomTarget<Drawable>() { .into(new CustomTarget<Drawable>() {
@ -266,7 +388,7 @@ public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleLi
holder.itemView.setOnCreateContextMenuListener((menu, v, menuInfo) -> { holder.itemView.setOnCreateContextMenuListener((menu, v, menuInfo) -> {
getMenuInflater().inflate(R.menu.context_menu_modules, menu); getMenuInflater().inflate(R.menu.context_menu_modules, menu);
menu.setHeaderTitle(item.getAppName()); menu.setHeaderTitle(item.getAppName());
Intent intent = AppHelper.getSettingsIntent(item.packageName, pm); Intent intent = AppHelper.getSettingsIntent(item.packageName, item.userId, pm);
if (intent == null) { if (intent == null) {
menu.removeItem(R.id.menu_launch); menu.removeItem(R.id.menu_launch);
} }
@ -278,11 +400,12 @@ public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleLi
holder.itemView.setOnClickListener(v -> { holder.itemView.setOnClickListener(v -> {
Intent intent = new Intent(ModulesActivity.this, AppListActivity.class); Intent intent = new Intent(ModulesActivity.this, AppListActivity.class);
intent.putExtra("modulePackageName", item.packageName); intent.putExtra("modulePackageName", item.packageName);
intent.putExtra("moduleUserId", item.userId);
startActivity(intent); startActivity(intent);
}); });
holder.itemView.setOnLongClickListener(v -> { holder.itemView.setOnLongClickListener(v -> {
selectedPackageName = item.packageName; selectedModule = item;
return false; return false;
}); });
@ -298,7 +421,8 @@ public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleLi
@Override @Override
public long getItemId(int position) { public long getItemId(int position) {
return showList.get(position).packageName.hashCode(); var module = showList.get(position);
return (module.packageName + "!" + module.userId).hashCode();
} }
@Override @Override
@ -318,7 +442,7 @@ public class ModulesActivity extends ListActivity implements ModuleUtil.ModuleLi
private final Runnable reloadModules = new Runnable() { private final Runnable reloadModules = new Runnable() {
public void run() { public void run() {
searchList.clear(); searchList.clear();
searchList.addAll(moduleUtil.getModules().values()); searchList.addAll(moduleUtil.getModules().values().stream().filter(module -> module.userId == userId).collect(Collectors.toList()));
Comparator<PackageInfo> cmp = AppHelper.getAppListComparator(0, pm); Comparator<PackageInfo> cmp = AppHelper.getAppListComparator(0, pm);
searchList.sort((a, b) -> { searchList.sort((a, b) -> {
boolean aChecked = moduleUtil.isModuleEnabled(a.packageName); boolean aChecked = moduleUtil.isModuleEnabled(a.packageName);

View File

@ -32,7 +32,6 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream; import java.util.zip.GZIPOutputStream;
@ -48,7 +47,7 @@ public class BackupUtils {
JSONObject rootObject = new JSONObject(); JSONObject rootObject = new JSONObject();
rootObject.put("version", VERSION); rootObject.put("version", VERSION);
JSONArray modulesArray = new JSONArray(); JSONArray modulesArray = new JSONArray();
Map<String, ModuleUtil.InstalledModule> modules = ModuleUtil.getInstance().getModules(); var modules = ModuleUtil.getInstance().getModules();
for (ModuleUtil.InstalledModule module : modules.values()) { for (ModuleUtil.InstalledModule module : modules.values()) {
if (packageName != null && !module.packageName.equals(packageName)) { if (packageName != null && !module.packageName.equals(packageName)) {
continue; continue;

View File

@ -27,6 +27,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build; import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import org.lsposed.manager.App; import org.lsposed.manager.App;
import org.lsposed.manager.ConfigManager; import org.lsposed.manager.ConfigManager;
@ -47,7 +48,7 @@ public final class ModuleUtil {
private final PackageManager pm; private final PackageManager pm;
private final List<ModuleListener> listeners = new CopyOnWriteArrayList<>(); private final List<ModuleListener> listeners = new CopyOnWriteArrayList<>();
private final HashSet<String> enabledModules; private final HashSet<String> enabledModules;
private Map<String, InstalledModule> installedModules; private Map<Pair<String, Integer>, InstalledModule> installedModules;
private boolean isReloading = false; private boolean isReloading = false;
private ModuleUtil() { private ModuleUtil() {
@ -82,15 +83,15 @@ public final class ModuleUtil {
isReloading = true; isReloading = true;
} }
Map<String, InstalledModule> modules = new HashMap<>(); Map<Pair<String, Integer>, InstalledModule> modules = new HashMap<>();
for (PackageInfo pkg : ConfigManager.getInstalledPackagesFromAllUsers(PackageManager.GET_META_DATA, false)) { for (PackageInfo pkg : ConfigManager.getInstalledPackagesFromAllUsers(PackageManager.GET_META_DATA, false)) {
ApplicationInfo app = pkg.applicationInfo; ApplicationInfo app = pkg.applicationInfo;
if (!app.enabled || app.uid / 100000 != 0) if (!app.enabled)
continue; continue;
if (app.metaData != null && app.metaData.containsKey("xposedminversion")) { if (app.metaData != null && app.metaData.containsKey("xposedminversion")) {
InstalledModule installed = new InstalledModule(pkg, false); 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; PackageInfo pkg;
try { try {
pkg = ConfigManager.getPackageInfo(packageName, PackageManager.GET_META_DATA); pkg = ConfigManager.getPackageInfo(packageName, PackageManager.GET_META_DATA, userId);
if (pkg == null) { if (pkg == null) {
throw new NameNotFoundException(); throw new NameNotFoundException();
} }
@ -112,7 +113,7 @@ public final class ModuleUtil {
InstalledModule old = installedModules.remove(packageName); InstalledModule old = installedModules.remove(packageName);
if (old != null) { if (old != null) {
for (ModuleListener listener : listeners) { for (ModuleListener listener : listeners) {
listener.onSingleInstalledModuleReloaded(instance, packageName, null); listener.onSingleInstalledModuleReloaded();
} }
} }
return null; return null;
@ -121,28 +122,31 @@ public final class ModuleUtil {
ApplicationInfo app = pkg.applicationInfo; ApplicationInfo app = pkg.applicationInfo;
if (app.enabled && app.metaData != null && app.metaData.containsKey("xposedminversion")) { if (app.enabled && app.metaData != null && app.metaData.containsKey("xposedminversion")) {
InstalledModule module = new InstalledModule(pkg, false); InstalledModule module = new InstalledModule(pkg, false);
installedModules.put(packageName, module); installedModules.put(Pair.create(packageName, userId), module);
for (ModuleListener listener : listeners) { for (ModuleListener listener : listeners) {
listener.onSingleInstalledModuleReloaded(instance, packageName, listener.onSingleInstalledModuleReloaded();
module);
} }
return module; return module;
} else { } else {
InstalledModule old = installedModules.remove(packageName); InstalledModule old = installedModules.remove(Pair.create(packageName, userId));
if (old != null) { if (old != null) {
for (ModuleListener listener : listeners) { for (ModuleListener listener : listeners) {
listener.onSingleInstalledModuleReloaded(instance, packageName, null); listener.onSingleInstalledModuleReloaded();
} }
} }
return null; return null;
} }
} }
public InstalledModule getModule(String packageName) { public InstalledModule getModule(String packageName, int userId) {
return installedModules.get(packageName); return installedModules.get(Pair.create(packageName, userId));
} }
public Map<String, InstalledModule> getModules() { public InstalledModule getModule(String packageName) {
return getModule(packageName, 0);
}
public Map<Pair<String, Integer>, InstalledModule> getModules() {
return installedModules; return installedModules;
} }
@ -180,11 +184,12 @@ public final class ModuleUtil {
* Called whenever one (previously or now) installed module has been * Called whenever one (previously or now) installed module has been
* reloaded * reloaded
*/ */
void onSingleInstalledModuleReloaded(ModuleUtil moduleUtil, String packageName, InstalledModule module); void onSingleInstalledModuleReloaded();
} }
public class InstalledModule { public class InstalledModule {
//private static final int FLAG_FORWARD_LOCK = 1 << 29; //private static final int FLAG_FORWARD_LOCK = 1 << 29;
public final int userId;
public final String packageName; public final String packageName;
public final String versionName; public final String versionName;
public final long versionCode; public final long versionCode;
@ -201,6 +206,7 @@ public final class ModuleUtil {
private InstalledModule(PackageInfo pkg, boolean isFramework) { private InstalledModule(PackageInfo pkg, boolean isFramework) {
this.app = pkg.applicationInfo; this.app = pkg.applicationInfo;
this.pkg = pkg; this.pkg = pkg;
this.userId = pkg.applicationInfo.uid / 100000;
this.packageName = pkg.packageName; this.packageName = pkg.packageName;
this.isFramework = isFramework; this.isFramework = isFramework;
this.versionName = pkg.versionName; this.versionName = pkg.versionName;

View File

@ -38,7 +38,7 @@ public final class NotificationUtil {
private static final int PENDING_INTENT_OPEN_APP_LIST = 0; private static final int PENDING_INTENT_OPEN_APP_LIST = 0;
private static final String NOTIFICATION_MODULES_CHANNEL = "modules_channel_2"; 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); NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel(NOTIFICATION_MODULES_CHANNEL, NotificationChannel channel = new NotificationChannel(NOTIFICATION_MODULES_CHANNEL,
@ -53,6 +53,7 @@ public final class NotificationUtil {
Intent intent = new Intent(context, AppListActivity.class) Intent intent = new Intent(context, AppListActivity.class)
.putExtra("modulePackageName", modulePackageName) .putExtra("modulePackageName", modulePackageName)
.putExtra("moduleUserId", moduleUserId)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent contentIntent = PendingIntent.getActivity(context, PENDING_INTENT_OPEN_APP_LIST, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); PendingIntent contentIntent = PendingIntent.getActivity(context, PENDING_INTENT_OPEN_APP_LIST, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -85,6 +85,7 @@
<string name="module_uninstall_message">Do you want to uninstall this module?</string> <string name="module_uninstall_message">Do you want to uninstall this module?</string>
<string name="module_uninstalled">Uninstalled %1$s</string> <string name="module_uninstalled">Uninstalled %1$s</string>
<string name="module_uninstall_failed">Uninstall unsuccessful</string> <string name="module_uninstall_failed">Uninstall unsuccessful</string>
<string name="user_title">User %d</string>
<!-- AppListActivity --> <!-- AppListActivity -->
<string name="compile_speed">Re-optimize</string> <string name="compile_speed">Re-optimize</string>

View File

@ -68,6 +68,8 @@ public class ConfigManager {
"android.permission.WRITE_SECURE_SETTINGS" "android.permission.WRITE_SECURE_SETTINGS"
}; };
private static final int PER_USER_RANGE = 100000;
static ConfigManager instance = null; static ConfigManager instance = null;
private static final File basePath = new File("/data/adb/lspd"); private static final File basePath = new File("/data/adb/lspd");
@ -312,9 +314,9 @@ public class ConfigManager {
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
String packageName = cursor.getString(pkgNameIdx); String packageName = cursor.getString(pkgNameIdx);
try { try {
PackageInfo pkgInfo = PackageService.getPackageInfo(packageName, 0, 0); PackageInfo pkgInfo = PackageService.getPackageInfoFromAllUsers(packageName, 0);
if (pkgInfo != null && pkgInfo.applicationInfo != null) { if (pkgInfo != null && pkgInfo.applicationInfo != null) {
cachedModule.put(pkgInfo.applicationInfo.uid, pkgInfo.packageName); cachedModule.put(pkgInfo.applicationInfo.uid % PER_USER_RANGE, pkgInfo.packageName);
} else { } else {
obsoleteModules.add(packageName); obsoleteModules.add(packageName);
} }
@ -635,7 +637,7 @@ public class ConfigManager {
} }
public boolean isModule(int uid) { 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 { private void recursivelyChown(File file, int uid, int gid) throws ErrnoException {

View File

@ -161,4 +161,9 @@ public class LSPManagerService extends ILSPManagerService.Stub {
public boolean isSepolicyLoaded() throws RemoteException { public boolean isSepolicyLoaded() throws RemoteException {
return ConfigManager.getInstance().isSepolicyLoaded(); return ConfigManager.getInstance().isSepolicyLoaded();
} }
@Override
public int[] getUsers() throws RemoteException {
return UserService.getUsers();
}
} }

View File

@ -102,8 +102,7 @@ public class LSPosedService extends ILSPosedService.Stub {
} }
ApplicationInfo applicationInfo = PackageService.getApplicationInfo(packageName, PackageManager.GET_META_DATA, 0); ApplicationInfo applicationInfo = PackageService.getApplicationInfo(packageName, PackageManager.GET_META_DATA, 0);
boolean isXposedModule = (userId == 0 || userId == -1) && boolean isXposedModule = applicationInfo != null &&
applicationInfo != null &&
applicationInfo.enabled && applicationInfo.enabled &&
applicationInfo.metaData != null && applicationInfo.metaData != null &&
applicationInfo.metaData.containsKey("xposedminversion"); applicationInfo.metaData.containsKey("xposedminversion");
@ -118,6 +117,7 @@ public class LSPosedService extends ILSPosedService.Stub {
broadcastIntent.addFlags(0x01000000); broadcastIntent.addFlags(0x01000000);
broadcastIntent.addFlags(0x00400000); broadcastIntent.addFlags(0x00400000);
broadcastIntent.setData(intent.getData()); broadcastIntent.setData(intent.getData());
broadcastIntent.putExtras(intent.getExtras());
broadcastIntent.setComponent(ComponentName.unflattenFromString(ConfigManager.getInstance().getManagerPackageName() + "/.receivers.ServiceReceiver")); broadcastIntent.setComponent(ComponentName.unflattenFromString(ConfigManager.getInstance().getManagerPackageName() + "/.receivers.ServiceReceiver"));
try { try {

View File

@ -96,6 +96,16 @@ public class PackageService {
return pm.getPackageInfo(packageName, flags, userId); 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 { public static ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) throws RemoteException {
IPackageManager pm = getPackageManager(); IPackageManager pm = getPackageManager();
if (pm == null) return null; if (pm == null) return null;

View File

@ -46,4 +46,6 @@ interface ILSPManagerService {
boolean uninstallPackage(String packageName) = 25; boolean uninstallPackage(String packageName) = 25;
boolean isSepolicyLoaded() = 26; boolean isSepolicyLoaded() = 26;
int[] getUsers() = 27;
} }