Add notification to open app
This commit is contained in:
parent
094ea66730
commit
2aec14482b
|
|
@ -91,7 +91,7 @@ public class ConfigManager {
|
|||
|
||||
private boolean verboseLog = true;
|
||||
private boolean dexObfuscate = false;
|
||||
private boolean autoAddShortcut = true;
|
||||
private boolean enableStatusNotification = true;
|
||||
private String miscPath = null;
|
||||
|
||||
private int managerUid = -1;
|
||||
|
|
@ -228,11 +228,13 @@ public class ConfigManager {
|
|||
dexObfuscate = bool != null && (boolean) bool;
|
||||
|
||||
bool = config.get("enable_auto_add_shortcut");
|
||||
if (bool == null) {
|
||||
updateModulePrefs("lspd", 0, "config", "enable_auto_add_shortcut", true);
|
||||
bool = true;
|
||||
if (bool != null) {
|
||||
// TODO: remove
|
||||
updateModulePrefs("lspd", 0, "config", "enable_auto_add_shortcut", null);
|
||||
}
|
||||
autoAddShortcut = (boolean) bool;
|
||||
|
||||
bool = config.get("enable_status_notification");
|
||||
enableStatusNotification = bool == null || (boolean) bool;
|
||||
|
||||
// Don't migrate to ConfigFileManager, as XSharedPreferences will be restored soon
|
||||
String string = (String) config.get("misc_path");
|
||||
|
|
@ -906,14 +908,14 @@ public class ConfigManager {
|
|||
return bool != null && (boolean) bool;
|
||||
}
|
||||
|
||||
public boolean isAddShortcut() {
|
||||
Log.d(TAG, "Auto add shortcut=" + autoAddShortcut);
|
||||
return autoAddShortcut;
|
||||
public boolean enableStatusNotification() {
|
||||
Log.d(TAG, "show status notification = " + enableStatusNotification);
|
||||
return enableStatusNotification;
|
||||
}
|
||||
|
||||
public void setAddShortcut(boolean on) {
|
||||
updateModulePrefs("lspd", 0, "config", "enable_auto_add_shortcut", on);
|
||||
this.autoAddShortcut = on;
|
||||
public void setEnableStatusNotification(boolean enable) {
|
||||
updateModulePrefs("lspd", 0, "config", "enable_status_notification", enable);
|
||||
enableStatusNotification = enable;
|
||||
}
|
||||
|
||||
public ParcelFileDescriptor getManagerApk() {
|
||||
|
|
|
|||
|
|
@ -24,33 +24,18 @@ import static org.lsposed.lspd.service.ServiceManager.TAG;
|
|||
import static org.lsposed.lspd.service.ServiceManager.getExecutorService;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.INotificationManager;
|
||||
import android.app.IServiceConnection;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.AttributionSource;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.pm.ShortcutManager;
|
||||
import android.content.pm.VersionedPackage;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
|
|
@ -64,19 +49,14 @@ import android.view.IWindowManager;
|
|||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.lsposed.daemon.BuildConfig;
|
||||
import org.lsposed.daemon.R;
|
||||
import org.lsposed.lspd.ILSPManagerService;
|
||||
import org.lsposed.lspd.models.Application;
|
||||
import org.lsposed.lspd.models.UserInfo;
|
||||
import org.lsposed.lspd.util.FakeContext;
|
||||
import org.lsposed.lspd.util.Utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
|
@ -88,23 +68,9 @@ import rikka.parcelablelist.ParcelableListSlice;
|
|||
public class LSPManagerService extends ILSPManagerService.Stub {
|
||||
// this maybe useful when obtaining the manager binder
|
||||
private static String RANDOM_UUID = null;
|
||||
private static final String SHORTCUT_ID = "org.lsposed.manager.shortcut";
|
||||
public static final String CHANNEL_ID = "lsposed";
|
||||
public static final String CHANNEL_NAME = "LSPosed Manager";
|
||||
public static final int CHANNEL_IMP = NotificationManager.IMPORTANCE_HIGH;
|
||||
|
||||
private static final HashMap<String, Integer> notificationIds = new HashMap<>();
|
||||
private static int previousNotificationId = 2000;
|
||||
|
||||
private static final HandlerThread worker = new HandlerThread("manager worker");
|
||||
private static final Handler workerHandler;
|
||||
private static Intent managerIntent = null;
|
||||
|
||||
static {
|
||||
worker.start();
|
||||
workerHandler = new Handler(worker.getLooper());
|
||||
}
|
||||
|
||||
public class ManagerGuard implements IBinder.DeathRecipient {
|
||||
private final @NonNull
|
||||
IBinder binder;
|
||||
|
|
@ -162,162 +128,47 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
|||
LSPManagerService() {
|
||||
}
|
||||
|
||||
private static Icon getIcon(int res) {
|
||||
var icon = ConfigFileManager.getResources().getDrawable(res, ConfigFileManager.getResources().newTheme());
|
||||
var bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(), icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||
icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
|
||||
icon.draw(new Canvas(bitmap));
|
||||
return Icon.createWithBitmap(bitmap);
|
||||
}
|
||||
|
||||
private static Icon getManagerIcon() {
|
||||
private static Intent getManagerIntent() {
|
||||
if (managerIntent != null) return managerIntent;
|
||||
try {
|
||||
return getIcon(R.mipmap.ic_launcher);
|
||||
} catch (Throwable e) {
|
||||
return getIcon(R.drawable.ic_launcher);
|
||||
}
|
||||
}
|
||||
|
||||
private static Icon getNotificationIcon() {
|
||||
return getIcon(R.drawable.ic_outline_extension_24);
|
||||
}
|
||||
|
||||
static Intent getManagerIntent() {
|
||||
try {
|
||||
if (managerIntent == null) {
|
||||
var intent = PackageService.getLaunchIntentForPackage(BuildConfig.MANAGER_INJECTED_PKG_NAME);
|
||||
if (intent == null) {
|
||||
var pkgInfo = PackageService.getPackageInfo(BuildConfig.MANAGER_INJECTED_PKG_NAME, PackageManager.GET_ACTIVITIES, 0);
|
||||
if (pkgInfo != null && pkgInfo.activities != null && pkgInfo.activities.length > 0) {
|
||||
for (var activityInfo : pkgInfo.activities) {
|
||||
if (activityInfo.processName.equals(activityInfo.packageName)) {
|
||||
intent = new Intent();
|
||||
intent.setComponent(new ComponentName(activityInfo.packageName, activityInfo.name));
|
||||
intent.setAction(Intent.ACTION_MAIN);
|
||||
break;
|
||||
}
|
||||
var intent = PackageService.getLaunchIntentForPackage(BuildConfig.MANAGER_INJECTED_PKG_NAME);
|
||||
if (intent == null) {
|
||||
var pkgInfo = PackageService.getPackageInfo(BuildConfig.MANAGER_INJECTED_PKG_NAME, PackageManager.GET_ACTIVITIES, 0);
|
||||
if (pkgInfo != null && pkgInfo.activities != null && pkgInfo.activities.length > 0) {
|
||||
for (var activityInfo : pkgInfo.activities) {
|
||||
if (activityInfo.processName.equals(activityInfo.packageName)) {
|
||||
intent = new Intent();
|
||||
intent.setComponent(new ComponentName(activityInfo.packageName, activityInfo.name));
|
||||
intent.setAction(Intent.ACTION_MAIN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (intent != null) {
|
||||
if (intent.getCategories() != null) intent.getCategories().clear();
|
||||
intent.addCategory("org.lsposed.manager.LAUNCH_MANAGER");
|
||||
intent.setPackage(BuildConfig.MANAGER_INJECTED_PKG_NAME);
|
||||
managerIntent = (Intent) intent.clone();
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
if (intent != null) {
|
||||
if (intent.getCategories() != null) intent.getCategories().clear();
|
||||
intent.addCategory("org.lsposed.manager.LAUNCH_MANAGER");
|
||||
intent.setPackage(BuildConfig.MANAGER_INJECTED_PKG_NAME);
|
||||
managerIntent = new Intent(intent);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "get Intent", e);
|
||||
}
|
||||
return managerIntent;
|
||||
|
||||
}
|
||||
|
||||
public static PendingIntent getNotificationIntent(String modulePackageName, int moduleUserId) {
|
||||
static void openManager(Uri withData) {
|
||||
var intent = getManagerIntent();
|
||||
if (intent == null) return;
|
||||
intent = new Intent(intent);
|
||||
intent.setData(withData);
|
||||
try {
|
||||
var intent = (Intent) getManagerIntent().clone();
|
||||
intent.setData(new Uri.Builder().scheme("module").encodedAuthority(modulePackageName + ":" + moduleUserId).build());
|
||||
return PendingIntent.getActivity(new FakeContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "get notification intent", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String getNotificationIdKey(String modulePackageName, int moduleUserId) {
|
||||
return modulePackageName + ":" + moduleUserId;
|
||||
}
|
||||
|
||||
private static int getAutoIncrementNotificationId() {
|
||||
// previousNotificationId start with 2001
|
||||
var idValue = previousNotificationId++;
|
||||
// Templates that may conflict with system ids after 2000
|
||||
// Copied from https://android.googlesource.com/platform/frameworks/base/+/master/proto/src/system_messages.proto
|
||||
var NOTE_NETWORK_AVAILABLE = 17303299;
|
||||
var NOTE_REMOTE_BUGREPORT = 678432343;
|
||||
var NOTE_STORAGE_PUBLIC = 0x53505542;
|
||||
var NOTE_STORAGE_PRIVATE = 0x53505256;
|
||||
var NOTE_STORAGE_DISK = 0x5344534b;
|
||||
var NOTE_STORAGE_MOVE = 0x534d4f56;
|
||||
// If auto created id is conflict, recreate it
|
||||
if (idValue == NOTE_NETWORK_AVAILABLE ||
|
||||
idValue == NOTE_REMOTE_BUGREPORT ||
|
||||
idValue == NOTE_STORAGE_PUBLIC ||
|
||||
idValue == NOTE_STORAGE_PRIVATE ||
|
||||
idValue == NOTE_STORAGE_DISK ||
|
||||
idValue == NOTE_STORAGE_MOVE) {
|
||||
return getAutoIncrementNotificationId();
|
||||
} else {
|
||||
return idValue;
|
||||
}
|
||||
}
|
||||
|
||||
private static int pushAndGetNotificationId(String modulePackageName, int moduleUserId) {
|
||||
var idKey = getNotificationIdKey(modulePackageName, moduleUserId);
|
||||
// If there is a new notification, put a new notification id into map
|
||||
return notificationIds.computeIfAbsent(idKey, key -> getAutoIncrementNotificationId());
|
||||
}
|
||||
|
||||
public static void showNotification(String modulePackageName,
|
||||
int moduleUserId,
|
||||
boolean enabled,
|
||||
boolean systemModule) {
|
||||
try {
|
||||
var context = new FakeContext();
|
||||
var userInfo = UserService.getUserInfo(moduleUserId);
|
||||
String userName = userInfo != null ? userInfo.name : String.valueOf(moduleUserId);
|
||||
String title = context.getString(enabled ? systemModule ?
|
||||
R.string.xposed_module_updated_notification_title_system :
|
||||
R.string.xposed_module_updated_notification_title :
|
||||
R.string.module_is_not_activated_yet);
|
||||
String content = context.getString(enabled ? systemModule ?
|
||||
R.string.xposed_module_updated_notification_content_system :
|
||||
R.string.xposed_module_updated_notification_content :
|
||||
(moduleUserId == 0 ?
|
||||
R.string.module_is_not_activated_yet_main_user_detailed :
|
||||
R.string.module_is_not_activated_yet_multi_user_detailed), modulePackageName, userName);
|
||||
|
||||
var style = new Notification.BigTextStyle();
|
||||
style.bigText(content);
|
||||
|
||||
var notification = new Notification.Builder(context, CHANNEL_ID)
|
||||
.setContentTitle(title)
|
||||
.setContentText(content)
|
||||
.setSmallIcon(getNotificationIcon())
|
||||
.setColor(Color.BLUE)
|
||||
.setContentIntent(getNotificationIntent(modulePackageName, moduleUserId))
|
||||
.setAutoCancel(true)
|
||||
.setStyle(style)
|
||||
.build();
|
||||
notification.extras.putString("android.substName", "LSPosed");
|
||||
var im = INotificationManager.Stub.asInterface(android.os.ServiceManager.getService("notification"));
|
||||
final NotificationChannel channel =
|
||||
new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, CHANNEL_IMP);
|
||||
im.createNotificationChannels("android",
|
||||
new ParceledListSlice<>(Collections.singletonList(channel)));
|
||||
im.enqueueNotificationWithTag("android", "android", modulePackageName,
|
||||
pushAndGetNotificationId(modulePackageName, moduleUserId),
|
||||
notification, 0);
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "post notification", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void cancelNotification(String modulePackageName, int moduleUserId) {
|
||||
try {
|
||||
var idKey = getNotificationIdKey(modulePackageName, moduleUserId);
|
||||
var idValue = notificationIds.get(idKey);
|
||||
if (idValue == null) return;
|
||||
var im = INotificationManager.Stub.asInterface(android.os.ServiceManager.getService("notification"));
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
im.cancelNotificationWithTag("android", "android", modulePackageName, idValue, 0);
|
||||
} else {
|
||||
im.cancelNotificationWithTag("android", modulePackageName, idValue, 0);
|
||||
}
|
||||
// Remove the notification id when the notification is canceled or current module app was uninstalled
|
||||
notificationIds.remove(idKey);
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "cancel notification", e);
|
||||
var userInfo = ActivityManagerService.getCurrentUser();
|
||||
if (userInfo == null || userInfo.id != 0) return;
|
||||
ActivityManagerService.startActivityAsUserWithFeature("android", null,
|
||||
intent, intent.getType(), null, null, 0, 0, null, null, 0);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "open manager", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -338,75 +189,11 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
|||
null, null, 0, null, null,
|
||||
null, -1, null, true, false,
|
||||
0);
|
||||
} catch (Throwable t) {
|
||||
} catch (RemoteException t) {
|
||||
Log.e(TAG, "Broadcast to manager failed: ", t);
|
||||
}
|
||||
}
|
||||
|
||||
public static void createOrUpdateShortcut(boolean force) {
|
||||
workerHandler.post(() -> createOrUpdateShortcutInternal(force, true));
|
||||
}
|
||||
|
||||
public static void createOrUpdateShortcut(boolean force, boolean shouldCreate) {
|
||||
workerHandler.post(() -> createOrUpdateShortcutInternal(force, shouldCreate));
|
||||
}
|
||||
|
||||
private synchronized static void createOrUpdateShortcutInternal(boolean force, boolean shouldCreate) {
|
||||
try {
|
||||
while (!UserService.isUserUnlocked(0)) {
|
||||
Log.d(TAG, "user is not yet unlocked, waiting for 1s...");
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
var smCtor = ShortcutManager.class.getDeclaredConstructor(Context.class);
|
||||
smCtor.setAccessible(true);
|
||||
var context = new FakeContext("com.android.settings");
|
||||
var sm = smCtor.newInstance(context);
|
||||
if (!sm.isRequestPinShortcutSupported()) {
|
||||
Log.d(TAG, "pinned shortcut not supported, skipping");
|
||||
return;
|
||||
}
|
||||
var intent = getManagerIntent();
|
||||
var settingIntent = PackageService.getLaunchIntentForPackage("com.android.settings");
|
||||
var componentName = settingIntent != null ? settingIntent.getComponent() : new ComponentName("com.android.settings", "android.__dummy__");
|
||||
var shortcut = new ShortcutInfo.Builder(context, SHORTCUT_ID)
|
||||
.setShortLabel("LSPosed")
|
||||
.setLongLabel("LSPosed")
|
||||
.setIntent(intent)
|
||||
.setActivity(componentName)
|
||||
.setCategories(intent.getCategories())
|
||||
.setIcon(getManagerIcon())
|
||||
.build();
|
||||
|
||||
for (var shortcutInfo : sm.getPinnedShortcuts()) {
|
||||
if (SHORTCUT_ID.equals(shortcutInfo.getId()) && shortcutInfo.isPinned()) {
|
||||
var shortcutIntent = sm.createShortcutResultIntent(shortcutInfo);
|
||||
var request = (LauncherApps.PinItemRequest) shortcutIntent.getParcelableExtra(LauncherApps.EXTRA_PIN_ITEM_REQUEST);
|
||||
var requestInfo = request.getShortcutInfo();
|
||||
// https://cs.android.com/android/platform/superproject/+/android-8.1.0_r1:frameworks/base/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java;drc=4ad6b57700bef4c484021f49e018117046562e6b;l=337
|
||||
if (requestInfo.isPinned()) {
|
||||
Log.d(TAG, "shortcut exists, updating");
|
||||
sm.updateShortcuts(Collections.singletonList(shortcut));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
var configManager = ConfigManager.getInstance();
|
||||
if (!force && configManager.isManagerInstalled()) {
|
||||
Log.d(TAG, "Manager has installed, skip adding shortcut");
|
||||
return;
|
||||
}
|
||||
// Only existing shortcuts are updated when system settings
|
||||
// are changed and no new shortcuts are requested
|
||||
if (!force && !shouldCreate) return;
|
||||
if (configManager.isAddShortcut()) {
|
||||
sm.requestPinShortcut(shortcut, null);
|
||||
Log.d(TAG, "done add shortcut");
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "add shortcut", e);
|
||||
}
|
||||
}
|
||||
|
||||
public ManagerGuard guardSnapshot() {
|
||||
var snapshot = guard;
|
||||
return snapshot != null && snapshot.isAlive() ? snapshot : null;
|
||||
|
|
@ -509,7 +296,7 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
|||
ActivityManagerService.forceStopPackage(packageName, 0);
|
||||
Log.d(TAG, "stopped old package");
|
||||
if (addUUID) {
|
||||
intent = (Intent) intent.clone();
|
||||
intent = new Intent(intent);
|
||||
RANDOM_UUID = UUID.randomUUID().toString();
|
||||
intent.addCategory(RANDOM_UUID);
|
||||
}
|
||||
|
|
@ -618,17 +405,6 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
|||
return ConfigManager.getInstance().disableModule(packageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAddShortcut() {
|
||||
return ConfigManager.getInstance().isAddShortcut();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAddShortcut(boolean enabled) {
|
||||
ConfigManager.getInstance().setAddShortcut(enabled);
|
||||
if (enabled) createOrUpdateShortcut(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVerboseLog() {
|
||||
return ConfigManager.getInstance().verboseLog();
|
||||
|
|
@ -646,7 +422,7 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
|||
|
||||
@Override
|
||||
public ParcelFileDescriptor getModulesLog() {
|
||||
workerHandler.post(() -> ServiceManager.getLogcatService().checkLogFile());
|
||||
ServiceManager.getLogcatService().checkLogFile();
|
||||
return ConfigManager.getInstance().getModulesLog();
|
||||
}
|
||||
|
||||
|
|
@ -674,10 +450,13 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
|||
@Override
|
||||
public boolean uninstallPackage(String packageName, int userId) throws RemoteException {
|
||||
try {
|
||||
if (ActivityManagerService.startUserInBackground(userId))
|
||||
return PackageService.uninstallPackage(new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST), userId);
|
||||
else return false;
|
||||
} catch (InterruptedException | InvocationTargetException | NoSuchMethodException | InstantiationException | IllegalAccessException e) {
|
||||
if (ActivityManagerService.startUserInBackground(userId)) {
|
||||
var pkg = new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST);
|
||||
return PackageService.uninstallPackage(pkg, userId);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (InterruptedException | ReflectiveOperationException e) {
|
||||
Log.e(TAG, e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -727,7 +506,7 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
|||
if (parent < 0) return -1;
|
||||
if (currentUser.id != parent) {
|
||||
if (!ActivityManagerService.switchUser(parent)) return -1;
|
||||
var window = android.os.ServiceManager.getService("window");
|
||||
var window = android.os.ServiceManager.getService(Context.WINDOW_SERVICE);
|
||||
if (window != null) {
|
||||
var wm = IWindowManager.Stub.asInterface(window);
|
||||
wm.lockNow(null);
|
||||
|
|
@ -759,9 +538,9 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
|||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
contentProvider.call(new AttributionSource.Builder(1000).setPackageName("android").build(),
|
||||
"settings", "PUT_global", "show_hidden_icon_apps_enabled", args);
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {
|
||||
contentProvider.call("android", null, "settings", "PUT_global", "show_hidden_icon_apps_enabled", args);
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
|
||||
contentProvider.call("android", "settings", "PUT_global", "show_hidden_icon_apps_enabled", args);
|
||||
}
|
||||
} catch (NoSuchMethodError e) {
|
||||
|
|
@ -784,12 +563,6 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
|||
stopAndStartActivity(BuildConfig.MANAGER_INJECTED_PKG_NAME, intent, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createShortcut() {
|
||||
createOrUpdateShortcut(true);
|
||||
setAddShortcut(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getDenyListPackages() {
|
||||
return ConfigManager.getInstance().getDenyListPackages();
|
||||
|
|
@ -826,6 +599,26 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
|||
PackageService.clearApplicationProfileData(packageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getLaunchIntentForManager() {
|
||||
return getManagerIntent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enableStatusNotification() {
|
||||
return ConfigManager.getInstance().enableStatusNotification();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnableStatusNotification(boolean enable) {
|
||||
ConfigManager.getInstance().setEnableStatusNotification(enable);
|
||||
if (enable) {
|
||||
LSPNotificationManager.notifyStatusNotification();
|
||||
} else {
|
||||
LSPNotificationManager.cancelStatusNotification();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performDexOptMode(String packageName) throws RemoteException {
|
||||
return PackageService.performDexOptMode(packageName);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,225 @@
|
|||
package org.lsposed.lspd.service;
|
||||
|
||||
import static org.lsposed.lspd.service.ServiceManager.TAG;
|
||||
|
||||
import android.app.INotificationManager;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.AdaptiveIconDrawable;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.graphics.drawable.LayerDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import org.lsposed.daemon.R;
|
||||
import org.lsposed.lspd.util.FakeContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
public class LSPNotificationManager {
|
||||
private static final String UPDATED_CHANNEL_ID = "lsposed_module_updated";
|
||||
private static final String STATUS_CHANNEL_ID = "lsposed_status";
|
||||
private static final int STATUS_NOTIFICATION_ID = 2000;
|
||||
|
||||
private static final HashMap<String, Integer> notificationIds = new HashMap<>();
|
||||
private static int previousNotificationId = STATUS_NOTIFICATION_ID;
|
||||
|
||||
static String openManagerAction = UUID.randomUUID().toString();
|
||||
|
||||
private static INotificationManager notificationManager = null;
|
||||
private static IBinder binder = null;
|
||||
|
||||
private static final IBinder.DeathRecipient recipient = new IBinder.DeathRecipient() {
|
||||
@Override
|
||||
public void binderDied() {
|
||||
Log.w(TAG, "nm is dead");
|
||||
binder.unlinkToDeath(this, 0);
|
||||
binder = null;
|
||||
notificationManager = null;
|
||||
}
|
||||
};
|
||||
|
||||
private static INotificationManager getNotificationManager() throws RemoteException {
|
||||
if (binder == null || notificationManager == null) {
|
||||
binder = android.os.ServiceManager.getService(Context.NOTIFICATION_SERVICE);
|
||||
binder.linkToDeath(recipient, 0);
|
||||
notificationManager = INotificationManager.Stub.asInterface(binder);
|
||||
}
|
||||
return notificationManager;
|
||||
}
|
||||
|
||||
private static Bitmap getBitmap(int id) {
|
||||
var r = ConfigFileManager.getResources();
|
||||
var res = r.getDrawable(id, r.newTheme());
|
||||
if (res instanceof BitmapDrawable) {
|
||||
return ((BitmapDrawable) res).getBitmap();
|
||||
} else {
|
||||
if (res instanceof AdaptiveIconDrawable) {
|
||||
var layers = new Drawable[]{((AdaptiveIconDrawable) res).getBackground(),
|
||||
((AdaptiveIconDrawable) res).getForeground()};
|
||||
res = new LayerDrawable(layers);
|
||||
}
|
||||
var bitmap = Bitmap.createBitmap(res.getIntrinsicWidth(),
|
||||
res.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||
var canvas = new Canvas(bitmap);
|
||||
res.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
res.draw(canvas);
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
|
||||
private static Icon getNotificationIcon() {
|
||||
return Icon.createWithBitmap(getBitmap(R.drawable.ic_notification));
|
||||
}
|
||||
|
||||
private static void createNotificationChannel(INotificationManager nm) throws RemoteException {
|
||||
var context = new FakeContext();
|
||||
var list = new ArrayList<NotificationChannel>();
|
||||
|
||||
var updated = new NotificationChannel(UPDATED_CHANNEL_ID,
|
||||
context.getString(R.string.module_updated_channel_name),
|
||||
NotificationManager.IMPORTANCE_HIGH);
|
||||
updated.setShowBadge(false);
|
||||
list.add(updated);
|
||||
|
||||
var status = new NotificationChannel(STATUS_CHANNEL_ID,
|
||||
context.getString(R.string.status_channel_name),
|
||||
NotificationManager.IMPORTANCE_MIN);
|
||||
status.setShowBadge(false);
|
||||
list.add(status);
|
||||
|
||||
nm.createNotificationChannelsForPackage("android", 1000, new ParceledListSlice<>(list));
|
||||
}
|
||||
|
||||
static void notifyStatusNotification() {
|
||||
var intent = new Intent(openManagerAction);
|
||||
var context = new FakeContext();
|
||||
int flags = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE;
|
||||
var notification = new Notification.Builder(context, STATUS_CHANNEL_ID)
|
||||
.setContentTitle(context.getString(R.string.lsposed_running_notification_title))
|
||||
.setContentText(context.getString(R.string.lsposed_running_notification_content))
|
||||
.setSmallIcon(getNotificationIcon())
|
||||
.setContentIntent(PendingIntent.getBroadcast(context, 1, intent, flags))
|
||||
.setVisibility(Notification.VISIBILITY_SECRET)
|
||||
.setColor(0xFFF48FB1)
|
||||
.setOngoing(true)
|
||||
.setAutoCancel(false)
|
||||
.build();
|
||||
notification.extras.putString("android.substName", "LSPosed");
|
||||
try {
|
||||
var nm = getNotificationManager();
|
||||
createNotificationChannel(nm);
|
||||
nm.enqueueNotificationWithTag("android", "android", null,
|
||||
STATUS_NOTIFICATION_ID, notification, 0);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "notifyStatusNotification: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
static void cancelStatusNotification() {
|
||||
try {
|
||||
var nm = getNotificationManager();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
nm.cancelNotificationWithTag("android", "android", null, STATUS_NOTIFICATION_ID, 0);
|
||||
} else {
|
||||
nm.cancelNotificationWithTag("android", null, STATUS_NOTIFICATION_ID, 0);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "cancelStatusNotification: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static PendingIntent getModuleIntent(String modulePackageName, int moduleUserId) {
|
||||
var intent = new Intent(openManagerAction);
|
||||
intent.setData(new Uri.Builder().scheme("module").encodedAuthority(modulePackageName + ":" + moduleUserId).build());
|
||||
int flags = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE;
|
||||
return PendingIntent.getBroadcast(new FakeContext(), 3, intent, flags);
|
||||
}
|
||||
|
||||
private static String getNotificationIdKey(String modulePackageName, int moduleUserId) {
|
||||
return modulePackageName + ":" + moduleUserId;
|
||||
}
|
||||
|
||||
private static int pushAndGetNotificationId(String modulePackageName, int moduleUserId) {
|
||||
var idKey = getNotificationIdKey(modulePackageName, moduleUserId);
|
||||
// previousNotificationId start with 2001
|
||||
// https://android.googlesource.com/platform/frameworks/base/+/master/proto/src/system_messages.proto
|
||||
// https://android.googlesource.com/platform/system/core/+/master/libcutils/include/private/android_filesystem_config.h
|
||||
// (AID_APP_END - AID_APP_START) x10 = 100000 < NOTE_NETWORK_AVAILABLE
|
||||
return notificationIds.computeIfAbsent(idKey, key -> previousNotificationId++);
|
||||
}
|
||||
|
||||
static void notifyModuleUpdated(String modulePackageName,
|
||||
int moduleUserId,
|
||||
boolean enabled,
|
||||
boolean systemModule) {
|
||||
try {
|
||||
var context = new FakeContext();
|
||||
var userInfo = UserService.getUserInfo(moduleUserId);
|
||||
String userName = userInfo != null ? userInfo.name : String.valueOf(moduleUserId);
|
||||
String title = context.getString(enabled ? systemModule ?
|
||||
R.string.xposed_module_updated_notification_title_system :
|
||||
R.string.xposed_module_updated_notification_title :
|
||||
R.string.module_is_not_activated_yet);
|
||||
String content = context.getString(enabled ? systemModule ?
|
||||
R.string.xposed_module_updated_notification_content_system :
|
||||
R.string.xposed_module_updated_notification_content :
|
||||
(moduleUserId == 0 ?
|
||||
R.string.module_is_not_activated_yet_main_user_detailed :
|
||||
R.string.module_is_not_activated_yet_multi_user_detailed), modulePackageName, userName);
|
||||
|
||||
var style = new Notification.BigTextStyle();
|
||||
style.bigText(content);
|
||||
|
||||
var notification = new Notification.Builder(context, UPDATED_CHANNEL_ID)
|
||||
.setContentTitle(title)
|
||||
.setContentText(content)
|
||||
.setSmallIcon(getNotificationIcon())
|
||||
.setContentIntent(getModuleIntent(modulePackageName, moduleUserId))
|
||||
.setVisibility(Notification.VISIBILITY_SECRET)
|
||||
.setColor(0xFFF48FB1)
|
||||
.setAutoCancel(true)
|
||||
.setStyle(style)
|
||||
.build();
|
||||
notification.extras.putString("android.substName", "LSPosed");
|
||||
var nm = getNotificationManager();
|
||||
createNotificationChannel(nm);
|
||||
nm.enqueueNotificationWithTag("android", "android", modulePackageName,
|
||||
pushAndGetNotificationId(modulePackageName, moduleUserId),
|
||||
notification, 0);
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "notify module updated", e);
|
||||
}
|
||||
}
|
||||
|
||||
static void cancelUpdatedNotification(String modulePackageName, int moduleUserId) {
|
||||
try {
|
||||
var idKey = getNotificationIdKey(modulePackageName, moduleUserId);
|
||||
var idValue = notificationIds.get(idKey);
|
||||
if (idValue == null) return;
|
||||
var nm = getNotificationManager();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
nm.cancelNotificationWithTag("android", "android", modulePackageName, idValue, 0);
|
||||
} else {
|
||||
nm.cancelNotificationWithTag("android", modulePackageName, idValue, 0);
|
||||
}
|
||||
notificationIds.remove(idKey);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "cancel notification", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -33,11 +33,14 @@ import android.net.Uri;
|
|||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import org.lsposed.daemon.BuildConfig;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class LSPosedService extends ILSPosedService.Stub {
|
||||
private static final int AID_NOBODY = 9999;
|
||||
|
|
@ -77,7 +80,7 @@ public class LSPosedService extends ILSPosedService.Stub {
|
|||
* However, PACKAGE_REMOVED will be triggered by `pm hide`, so we use UID_REMOVED instead.
|
||||
*/
|
||||
|
||||
synchronized public void dispatchPackageChanged(Intent intent) {
|
||||
private void dispatchPackageChanged(Intent intent) {
|
||||
if (intent == null) return;
|
||||
int uid = intent.getIntExtra(Intent.EXTRA_UID, AID_NOBODY);
|
||||
if (uid == AID_NOBODY || uid <= 0) return;
|
||||
|
|
@ -109,13 +112,15 @@ public class LSPosedService extends ILSPosedService.Stub {
|
|||
isXposedModule = true;
|
||||
broadcastAndShowNotification(moduleName, userId, intent, true);
|
||||
}
|
||||
// Anyway, canceled the notification
|
||||
if (moduleName != null) LSPManagerService.cancelNotification(moduleName, userId);
|
||||
if (moduleName != null) {
|
||||
LSPNotificationManager.cancelUpdatedNotification(moduleName, userId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Intent.ACTION_PACKAGE_REMOVED:
|
||||
// Anyway, canceled the notification
|
||||
if (moduleName != null) LSPManagerService.cancelNotification(moduleName, userId);
|
||||
if (moduleName != null) {
|
||||
LSPNotificationManager.cancelUpdatedNotification(moduleName, userId);
|
||||
}
|
||||
break;
|
||||
case Intent.ACTION_PACKAGE_ADDED:
|
||||
case Intent.ACTION_PACKAGE_CHANGED: {
|
||||
|
|
@ -159,12 +164,7 @@ public class LSPosedService extends ILSPosedService.Stub {
|
|||
|
||||
if (BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME.equals(moduleName) && userId == 0) {
|
||||
Log.d(TAG, "Manager updated");
|
||||
try {
|
||||
ConfigManager.getInstance().updateManager(removed);
|
||||
LSPManagerService.createOrUpdateShortcut(false);
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
ConfigManager.getInstance().updateManager(removed);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -183,225 +183,136 @@ public class LSPosedService extends ILSPosedService.Stub {
|
|||
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);
|
||||
LSPNotificationManager.notifyModuleUpdated(packageName, userId, enabled, systemModule);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized public void dispatchUserChanged(Intent intent) {
|
||||
private void dispatchUserChanged(Intent intent) {
|
||||
if (intent == null) return;
|
||||
int uid = intent.getIntExtra(EXTRA_USER_HANDLE, AID_NOBODY);
|
||||
if (uid == AID_NOBODY || uid <= 0) return;
|
||||
LSPManagerService.broadcastIntent(intent);
|
||||
}
|
||||
|
||||
private void dispatchBootCompleted(Intent intent) {
|
||||
try {
|
||||
LSPManagerService.broadcastIntent(intent);
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "dispatch user info changed", e);
|
||||
var am = ActivityManagerService.getActivityManager();
|
||||
if (am != null) am.setActivityController(null, false);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "setActivityController", e);
|
||||
}
|
||||
var configManager = ConfigManager.getInstance();
|
||||
if (configManager.enableStatusNotification()) {
|
||||
LSPNotificationManager.notifyStatusNotification();
|
||||
}
|
||||
}
|
||||
|
||||
synchronized public void dispatchUserUnlocked(Intent intent) {
|
||||
try {
|
||||
LSPManagerService.createOrUpdateShortcut(false);
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "dispatch user unlocked", e);
|
||||
}
|
||||
private void dispatchConfigurationChanged(Intent intent) {
|
||||
ConfigFileManager.reloadConfiguration();
|
||||
}
|
||||
|
||||
synchronized public void dispatchConfigurationChanged(Intent intent) {
|
||||
try {
|
||||
ConfigFileManager.reloadConfiguration();
|
||||
LSPManagerService.createOrUpdateShortcut(false, false);
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "dispatch configuration changed", e);
|
||||
}
|
||||
private void dispatchSecretCodeReceive(Intent i) {
|
||||
LSPManagerService.openManager(null);
|
||||
}
|
||||
|
||||
synchronized public void dispatchSecretCodeReceive() {
|
||||
Intent intent = LSPManagerService.getManagerIntent();
|
||||
try {
|
||||
var userInfo = ActivityManagerService.getCurrentUser();
|
||||
if (userInfo != null) {
|
||||
var userId = userInfo.id;
|
||||
if (userId == 0) {
|
||||
ActivityManagerService.startActivityAsUserWithFeature("android", null,
|
||||
intent, intent.getType(), null, null, 0, 0, null, null, userId);
|
||||
LSPManagerService.createOrUpdateShortcut(false);
|
||||
private void dispatchOpenManager(Intent intent) {
|
||||
LSPManagerService.openManager(intent.getData());
|
||||
}
|
||||
|
||||
private void registerReceiver(List<IntentFilter> filters, String requiredPermission, int userId, Consumer<Intent> task) {
|
||||
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(() -> task.accept(intent));
|
||||
if (!ordered) return;
|
||||
try {
|
||||
ActivityManagerService.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "finish receiver", e);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "dispatch secret code received", e);
|
||||
};
|
||||
try {
|
||||
for (var filter : filters) {
|
||||
ActivityManagerService.registerReceiver("android", null, receiver, filter, requiredPermission, userId, 0);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "register receiver", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerReceiver(List<IntentFilter> filters, int userId, Consumer<Intent> task) {
|
||||
registerReceiver(filters, null, userId, task);
|
||||
}
|
||||
|
||||
private void registerPackageReceiver() {
|
||||
try {
|
||||
IntentFilter packageFilter = new IntentFilter();
|
||||
packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||
packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
|
||||
packageFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
|
||||
packageFilter.addDataScheme("package");
|
||||
var packageFilter = new IntentFilter();
|
||||
packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||
packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
|
||||
packageFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
|
||||
packageFilter.addDataScheme("package");
|
||||
|
||||
IntentFilter uidFilter = new IntentFilter();
|
||||
uidFilter.addAction(Intent.ACTION_UID_REMOVED);
|
||||
var uidFilter = new IntentFilter(Intent.ACTION_UID_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(() -> dispatchPackageChanged(intent));
|
||||
if (!ordered) return;
|
||||
try {
|
||||
ActivityManagerService.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "finish receiver", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ActivityManagerService.registerReceiver("android", null, receiver, packageFilter, null, -1, 0);
|
||||
ActivityManagerService.registerReceiver("android", null, receiver, uidFilter, null, -1, 0);
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "register package receiver", e);
|
||||
}
|
||||
registerReceiver(List.of(packageFilter, uidFilter), -1, this::dispatchPackageChanged);
|
||||
Log.d(TAG, "registered package receiver");
|
||||
}
|
||||
|
||||
private void registerUnlockReceiver() {
|
||||
try {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
|
||||
|
||||
ActivityManagerService.registerReceiver("android", null, new IIntentReceiver.Stub() {
|
||||
@Override
|
||||
public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
|
||||
getExecutorService().submit(() -> dispatchUserUnlocked(intent));
|
||||
if (!ordered) return;
|
||||
try {
|
||||
ActivityManagerService.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "finish receiver", e);
|
||||
}
|
||||
}
|
||||
}, intentFilter, null, 0, 0);
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "register unlock receiver", e);
|
||||
}
|
||||
Log.d(TAG, "registered unlock receiver");
|
||||
}
|
||||
|
||||
private void registerConfigurationReceiver() {
|
||||
try {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
|
||||
var intentFilter = new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED);
|
||||
|
||||
ActivityManagerService.registerReceiver("android", null, new IIntentReceiver.Stub() {
|
||||
@Override
|
||||
public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
|
||||
getExecutorService().submit(() -> dispatchConfigurationChanged(intent));
|
||||
if (!ordered) return;
|
||||
try {
|
||||
ActivityManagerService.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "finish receiver", e);
|
||||
}
|
||||
}
|
||||
}, intentFilter, null, 0, 0);
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "register configuration receiver", e);
|
||||
}
|
||||
registerReceiver(List.of(intentFilter), 0, this::dispatchConfigurationChanged);
|
||||
Log.d(TAG, "registered configuration receiver");
|
||||
}
|
||||
|
||||
private void registerSecretCodeReceiver() {
|
||||
try {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction("android.provider.Telephony.SECRET_CODE");
|
||||
intentFilter.addDataAuthority("5776733", null);
|
||||
intentFilter.addDataScheme("android_secret_code");
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction("android.provider.Telephony.SECRET_CODE");
|
||||
intentFilter.addAction("android.telephony.action.SECRET_CODE");
|
||||
intentFilter.addDataAuthority("5776733", null);
|
||||
intentFilter.addDataScheme("android_secret_code");
|
||||
|
||||
ActivityManagerService.registerReceiver("android", null, new IIntentReceiver.Stub() {
|
||||
@Override
|
||||
public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
|
||||
getExecutorService().submit(() -> dispatchSecretCodeReceive());
|
||||
if (!ordered) return;
|
||||
try {
|
||||
ActivityManagerService.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "finish receiver", e);
|
||||
}
|
||||
}
|
||||
}, intentFilter, null, 0, 0);
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "register secret code receiver", e);
|
||||
}
|
||||
registerReceiver(List.of(intentFilter), 0, this::dispatchSecretCodeReceive);
|
||||
Log.d(TAG, "registered secret code receiver");
|
||||
}
|
||||
|
||||
private void registerBootCompleteReceiver() {
|
||||
try {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED);
|
||||
var intentFilter = new IntentFilter(Intent.ACTION_LOCKED_BOOT_COMPLETED);
|
||||
|
||||
ActivityManagerService.registerReceiver("android", null, new IIntentReceiver.Stub() {
|
||||
@Override
|
||||
public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
|
||||
getExecutorService().submit(() -> {
|
||||
try {
|
||||
var am = ActivityManagerService.getActivityManager();
|
||||
if (am != null) am.setActivityController(null, false);
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "setActivityController", e);
|
||||
}
|
||||
});
|
||||
if (!ordered) return;
|
||||
try {
|
||||
ActivityManagerService.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "finish receiver", e);
|
||||
}
|
||||
}
|
||||
}, intentFilter, null, 0, 0);
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "register boot receiver", e);
|
||||
}
|
||||
registerReceiver(List.of(intentFilter), 0, this::dispatchBootCompleted);
|
||||
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 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));
|
||||
if (!ordered) return;
|
||||
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);
|
||||
}
|
||||
registerReceiver(List.of(userFilter), -1, this::dispatchUserChanged);
|
||||
Log.d(TAG, "registered user info change receiver");
|
||||
}
|
||||
|
||||
private void registerOpenManagerReceiver() {
|
||||
var intentFilter = new IntentFilter(LSPNotificationManager.openManagerAction);
|
||||
var moduleFilter = new IntentFilter(intentFilter);
|
||||
moduleFilter.addDataScheme("module");
|
||||
|
||||
registerReceiver(List.of(intentFilter, moduleFilter),
|
||||
"android.permission.BRICK", 0, this::dispatchOpenManager);
|
||||
Log.d(TAG, "registered open manager receiver");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchSystemServerContext(IBinder activityThread, IBinder activityToken, String api) {
|
||||
Log.d(TAG, "received system context");
|
||||
ConfigManager.getInstance().setApi(api);
|
||||
ActivityManagerService.onSystemServerContext(IApplicationThread.Stub.asInterface(activityThread), activityToken);
|
||||
registerPackageReceiver();
|
||||
registerUnlockReceiver();
|
||||
registerConfigurationReceiver();
|
||||
registerSecretCodeReceiver();
|
||||
registerBootCompleteReceiver();
|
||||
registerUserChangeReceiver();
|
||||
registerOpenManagerReceiver();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ public class ServiceManager {
|
|||
private static LogcatService logcatService = null;
|
||||
private static Dex2OatService dex2OatService = null;
|
||||
|
||||
private static final ExecutorService executorService = Executors.newCachedThreadPool();
|
||||
private static final ExecutorService executorService = Executors.newSingleThreadExecutor();
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
public static Dex2OatService getDex2OatService() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ This file is part of LSPosed.
|
||||
~
|
||||
~ LSPosed is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ LSPosed is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
|
||||
~
|
||||
~ Copyright (C) 2022 LSPosed Contributors
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M17.73,12.02l3.98,-3.98c0.39,-0.39 0.39,-1.02 0,-1.41l-4.34,-4.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-3.98,3.98L8,2.29C7.8,2.1 7.55,2 7.29,2c-0.25,0 -0.51,0.1 -0.7,0.29L2.25,6.63c-0.39,0.39 -0.39,1.02 0,1.41l3.98,3.98L2.25,16c-0.39,0.39 -0.39,1.02 0,1.41l4.34,4.34c0.39,0.39 1.02,0.39 1.41,0l3.98,-3.98 3.98,3.98c0.2,0.2 0.45,0.29 0.71,0.29 0.26,0 0.51,-0.1 0.71,-0.29l4.34,-4.34c0.39,-0.39 0.39,-1.02 0,-1.41l-3.99,-3.98zM12,9c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM7.29,10.96L3.66,7.34l3.63,-3.63 3.62,3.62 -3.62,3.63zM10,13c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM12,15c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM14,11c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM16.66,20.34l-3.63,-3.62 3.63,-3.63 3.62,3.62 -3.62,3.63z" />
|
||||
</vector>
|
||||
|
|
@ -7,4 +7,8 @@
|
|||
<string name="xposed_module_updated_notification_content">%s has been updated, please force stop and restart apps in its scope</string>
|
||||
<string name="xposed_module_updated_notification_title_system">Xposed module updated, system reboot required</string>
|
||||
<string name="xposed_module_updated_notification_content_system">%s has been updated, since the scope contains System Framework, required reboot to apply changes</string>
|
||||
<string name="module_updated_channel_name">Module update complete</string>
|
||||
<string name="status_channel_name">LSPosed status</string>
|
||||
<string name="lsposed_running_notification_title">LSPosed loaded</string>
|
||||
<string name="lsposed_running_notification_content">Tap the notification to open manager</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,12 @@ public interface INotificationManager extends IInterface {
|
|||
@RequiresApi(30)
|
||||
void cancelNotificationWithTag(String pkg, String opPkg, String tag, int id, int userId) throws RemoteException;
|
||||
|
||||
void createNotificationChannels(String pkg, ParceledListSlice<NotificationChannel> channelsList) throws RemoteException;
|
||||
void createNotificationChannelsForPackage(String pkg, int uid, ParceledListSlice<NotificationChannel> channelsList) throws RemoteException;
|
||||
|
||||
@RequiresApi(30)
|
||||
NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, String conversationId, boolean includeDeleted) throws RemoteException;
|
||||
|
||||
NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, boolean includeDeleted) throws RemoteException;
|
||||
|
||||
abstract class Stub extends Binder implements INotificationManager {
|
||||
public static INotificationManager asInterface(IBinder obj) {
|
||||
|
|
|
|||
|
|
@ -70,12 +70,6 @@ interface ILSPManagerService {
|
|||
|
||||
void restartFor(in Intent intent) = 35;
|
||||
|
||||
void createShortcut() = 36;
|
||||
|
||||
boolean isAddShortcut() = 37;
|
||||
|
||||
void setAddShortcut(boolean enabled) = 38;
|
||||
|
||||
oneway void flashZip(String zipPath, in ParcelFileDescriptor outputStream) = 39;
|
||||
|
||||
boolean performDexOptMode(String packageName) = 40;
|
||||
|
|
@ -89,4 +83,10 @@ interface ILSPManagerService {
|
|||
int getDex2OatWrapperCompatibility() = 44;
|
||||
|
||||
void clearApplicationProfileData(in String packageName) = 45;
|
||||
|
||||
Intent getLaunchIntentForManager() = 46;
|
||||
|
||||
boolean enableStatusNotification() = 47;
|
||||
|
||||
void setEnableStatusNotification(boolean enable) = 48;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue