[core] Auto install manager when invalid
This commit is contained in:
parent
e857719a96
commit
cc6155b496
|
|
@ -39,6 +39,7 @@ val repo = FileRepository(rootProject.file(".git"))
|
|||
val refId = repo.refDatabase.exactRef("refs/remotes/origin/master").objectId!!
|
||||
val commitCount = Git(repo).log().add(refId).call().count()
|
||||
|
||||
val defaultManagerPackageName by extra("io.github.lsposed.manager")
|
||||
val verCode by extra(commitCount + 4200)
|
||||
val verName by extra("v1.2.0")
|
||||
val androidTargetSdkVersion by extra(30)
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ val moduleMinRiruApiVersion = 10
|
|||
val moduleMinRiruVersionName = "v23.0"
|
||||
val moduleMaxRiruApiVersion = 10
|
||||
|
||||
val defaultManagerPackageName: String by rootProject.extra
|
||||
val apiCode: Int by rootProject.extra
|
||||
|
||||
val androidTargetSdkVersion: Int by rootProject.extra
|
||||
|
|
@ -102,6 +103,7 @@ android {
|
|||
buildConfigField("int", "API_CODE", "$apiCode")
|
||||
buildConfigField("String", "VERSION_NAME", "\"$verName\"")
|
||||
buildConfigField("Integer", "VERSION_CODE", verCode.toString())
|
||||
buildConfigField("String", "DEFAULT_MANAGER_PACKAGE_NAME", "\"$defaultManagerPackageName\"")
|
||||
}
|
||||
|
||||
lint {
|
||||
|
|
|
|||
|
|
@ -6,4 +6,5 @@ interface ILSPosedService {
|
|||
ILSPApplicationService requestApplicationService(int uid, int pid, String processName, IBinder heartBeat) = 1;
|
||||
|
||||
oneway void dispatchPackageChanged(in Intent intent) = 2;
|
||||
}
|
||||
oneway void dispatchBootCompleted(in Intent intent) = 3;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
package io.github.lsposed.lspd.service;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.ActivityThread;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import io.github.lsposed.lspd.util.Utils;
|
||||
|
||||
public class BootReceiver {
|
||||
public static void register(BroadcastReceiver receiver) {
|
||||
ActivityThread activityThread = ActivityThread.currentActivityThread();
|
||||
if (activityThread == null) {
|
||||
Utils.logW("ActivityThread is null");
|
||||
return;
|
||||
}
|
||||
Context context = activityThread.getSystemContext();
|
||||
if (context == null) {
|
||||
Utils.logW("context is null");
|
||||
return;
|
||||
}
|
||||
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED);
|
||||
|
||||
HandlerThread thread = new HandlerThread("lspd-BootReceiver");
|
||||
thread.start();
|
||||
Handler handler = new Handler(thread.getLooper());
|
||||
|
||||
try {
|
||||
@SuppressLint("DiscouragedPrivateApi")
|
||||
Method method = Context.class.getDeclaredMethod("registerReceiver", BroadcastReceiver.class, IntentFilter.class, String.class, Handler.class);
|
||||
method.invoke(context, receiver, intentFilter, null, handler);
|
||||
Utils.logI("registered package receiver");
|
||||
} catch (Throwable e) {
|
||||
Utils.logW("registerReceiver failed", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -206,6 +206,16 @@ public class BridgeService {
|
|||
}
|
||||
}
|
||||
});
|
||||
BootReceiver.register(new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
try {
|
||||
service.dispatchBootCompleted(intent);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
serviceBinder.unlinkToDeath(LSPSERVICE_DEATH_RECIPIENT, 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ import java.util.Set;
|
|||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import io.github.lsposed.lspd.Application;
|
||||
import io.github.lsposed.lspd.BuildConfig;
|
||||
|
||||
import static io.github.lsposed.lspd.service.ServiceManager.TAG;
|
||||
|
||||
|
|
@ -81,8 +82,6 @@ public class ConfigManager {
|
|||
private static final File verboseLogSwitch = new File(configPath, "verbose_log");
|
||||
private boolean verboseLog = false;
|
||||
|
||||
private static final String DEFAULT_MANAGER_PACKAGE_NAME = "io.github.lsposed.manager";
|
||||
|
||||
private static final File managerPath = new File(configPath, "manager");
|
||||
private String manager = null;
|
||||
private int managerUid = -1;
|
||||
|
|
@ -226,7 +225,7 @@ public class ConfigManager {
|
|||
public synchronized void updateManager() {
|
||||
if (!packageStarted) return;
|
||||
try {
|
||||
PackageInfo info = PackageService.getPackageInfo(readText(managerPath, DEFAULT_MANAGER_PACKAGE_NAME), 0, 0);
|
||||
PackageInfo info = PackageService.getPackageInfo(readText(managerPath, BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME), 0, 0);
|
||||
if (info != null) {
|
||||
managerUid = info.applicationInfo.uid;
|
||||
manager = info.packageName;
|
||||
|
|
@ -237,6 +236,15 @@ public class ConfigManager {
|
|||
}
|
||||
}
|
||||
|
||||
public void ensureManager() {
|
||||
if (!packageStarted) return;
|
||||
new Thread(() -> {
|
||||
if (PackageService.installManagerIfAbsent(manager, new File("/data/adb/lspd/base.apk"))) {
|
||||
updateManager(BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public synchronized void updateManager(@NonNull String packageName) {
|
||||
writeText(managerPath, packageName);
|
||||
manager = packageName;
|
||||
|
|
@ -610,7 +618,7 @@ public class ConfigManager {
|
|||
}
|
||||
|
||||
public boolean isManager(String packageName) {
|
||||
return packageName.equals(manager) || packageName.equals(DEFAULT_MANAGER_PACKAGE_NAME);
|
||||
return packageName.equals(manager) || packageName.equals(BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME);
|
||||
}
|
||||
|
||||
public boolean isManager(int uid) {
|
||||
|
|
@ -627,7 +635,7 @@ public class ConfigManager {
|
|||
|
||||
public static void grantManagerPermission() {
|
||||
try {
|
||||
PackageService.grantRuntimePermission(readText(managerPath, DEFAULT_MANAGER_PACKAGE_NAME), "android.permission.INTERACT_ACROSS_USERS", 0);
|
||||
PackageService.grantRuntimePermission(readText(managerPath, BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME), "android.permission.INTERACT_ACROSS_USERS", 0);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import android.util.Log;
|
|||
import java.util.Arrays;
|
||||
|
||||
import io.github.lsposed.lspd.Application;
|
||||
import pxb.android.arsc.Config;
|
||||
|
||||
import static io.github.lsposed.lspd.service.ServiceManager.TAG;
|
||||
|
||||
|
|
@ -122,4 +123,9 @@ public class LSPosedService extends ILSPosedService.Stub {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void dispatchBootCompleted(Intent intent) throws RemoteException {
|
||||
ConfigManager.getInstance().ensureManager();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,12 +19,20 @@
|
|||
|
||||
package io.github.lsposed.lspd.service;
|
||||
|
||||
import android.content.IIntentReceiver;
|
||||
import android.content.IIntentSender;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ComponentInfo;
|
||||
import android.content.pm.IPackageInstaller;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageInstaller;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.pm.VersionedPackage;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
|
|
@ -32,14 +40,23 @@ import android.util.Log;
|
|||
import android.util.Pair;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import hidden.HiddenApiBridge;
|
||||
import io.github.lsposed.lspd.Application;
|
||||
import io.github.lsposed.lspd.BuildConfig;
|
||||
import io.github.lsposed.lspd.util.InstallerVerifier;
|
||||
import io.github.lsposed.lspd.utils.ParceledListSlice;
|
||||
|
||||
import static android.content.pm.ServiceInfo.FLAG_ISOLATED_PROCESS;
|
||||
|
|
@ -145,6 +162,8 @@ public class PackageService {
|
|||
}
|
||||
|
||||
private static PackageInfo getPackageInfoWithComponents(String packageName, int flags, int userId) throws RemoteException {
|
||||
IPackageManager pm = getPackageManager();
|
||||
if (pm == null) return null;
|
||||
PackageInfo pkgInfo;
|
||||
try {
|
||||
pkgInfo = pm.getPackageInfo(packageName, flags | PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES | PackageManager.GET_RECEIVERS | PackageManager.GET_PROVIDERS, userId);
|
||||
|
|
@ -176,4 +195,87 @@ public class PackageService {
|
|||
return null;
|
||||
return pkgInfo;
|
||||
}
|
||||
|
||||
static abstract class IntentSenderAdaptor extends IIntentSender.Stub {
|
||||
public abstract void send(Intent intent);
|
||||
|
||||
@Override
|
||||
public int send(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
|
||||
send(intent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
|
||||
send(intent);
|
||||
}
|
||||
|
||||
public IntentSender getIntentSender() throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
|
||||
@SuppressWarnings("JavaReflectionMemberAccess")
|
||||
Constructor<IntentSender> intentSenderConstructor = IntentSender.class.getConstructor(IIntentSender.class);
|
||||
intentSenderConstructor.setAccessible(true);
|
||||
return intentSenderConstructor.newInstance(this);
|
||||
}
|
||||
}
|
||||
|
||||
private static void uninstallPackage(VersionedPackage versionedPackage) throws RemoteException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
pm.getPackageInstaller().uninstallExistingPackage(versionedPackage, "com.android.shell", new IntentSenderAdaptor() {
|
||||
@Override
|
||||
public void send(Intent intent) {
|
||||
latch.countDown();
|
||||
}
|
||||
}.getIntentSender(), 0);
|
||||
latch.await();
|
||||
}
|
||||
|
||||
public static synchronized boolean installManagerIfAbsent(String packageName, File apkFile) {
|
||||
IPackageManager pm = getPackageManager();
|
||||
if (pm == null) return false;
|
||||
|
||||
try {
|
||||
PackageInfo pkgInfo = pm.getPackageInfo(packageName, 0, 0);
|
||||
if (pkgInfo != null && pkgInfo.versionName != null && pkgInfo.versionName.equals(BuildConfig.VERSION_NAME) && pkgInfo.applicationInfo != null && InstallerVerifier.verifyInstallerSignature(pkgInfo.applicationInfo))
|
||||
return false;
|
||||
// manager is not installed or version not matched, install stub
|
||||
if (pkgInfo != null) {
|
||||
uninstallPackage(new VersionedPackage(pkgInfo.packageName, pkgInfo.versionCode));
|
||||
}
|
||||
IPackageInstaller installerService = pm.getPackageInstaller();
|
||||
String installerPackageName = "com.android.shell";
|
||||
@SuppressWarnings("JavaReflectionMemberAccess")
|
||||
Constructor<PackageInstaller> installerConstructor = PackageInstaller.class.getConstructor(IPackageInstaller.class, String.class, int.class);
|
||||
installerConstructor.setAccessible(true);
|
||||
PackageInstaller installer = installerConstructor.newInstance(installerService, installerPackageName, 0);
|
||||
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
|
||||
int installFlags = HiddenApiBridge.PackageInstaller_SessionParams_installFlags(params);
|
||||
installFlags |= 0x00000004/*PackageManager.INSTALL_ALLOW_TEST*/ | 0x00000002/*PackageManager.INSTALL_REPLACE_EXISTING*/;
|
||||
HiddenApiBridge.PackageInstaller_SessionParams_installFlags(params, installFlags);
|
||||
|
||||
int sessionId = installer.createSession(params);
|
||||
try (PackageInstaller.Session session = installer.openSession(sessionId)) {
|
||||
try (InputStream is = new FileInputStream(apkFile); OutputStream os = session.openWrite(apkFile.getName(), 0, -1)) {
|
||||
byte[] buf = new byte[8192];
|
||||
int len;
|
||||
while ((len = is.read(buf)) > 0) {
|
||||
os.write(buf, 0, len);
|
||||
os.flush();
|
||||
session.fsync(os);
|
||||
}
|
||||
}
|
||||
session.commit(new IntentSenderAdaptor() {
|
||||
@Override
|
||||
public void send(Intent result) {
|
||||
int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
|
||||
String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
|
||||
Log.d(TAG, status + " " + message);
|
||||
}
|
||||
}.getIntentSender());
|
||||
}
|
||||
return true;
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -192,11 +192,6 @@ else
|
|||
ui_print " - ${LANG_CUST_INST_CONF_NEW} ${MISC_RAND}"
|
||||
mkdir -p /data/adb/lspd || abortC "! ${LANG_CUST_ERR_CONF_CREATE}"
|
||||
echo "$MISC_PATH" > /data/adb/lspd/misc_path || abortC "! ${LANG_CUST_ERR_CONF_STORE}"
|
||||
if [[ -d /data/user_de/0/io.github.lsposed.manager/conf/ ]]; then
|
||||
mkdir -p /data/misc/$MISC_PATH/0/conf
|
||||
cp -r /data/user_de/0/io.github.lsposed.manager/conf/* /data/misc/$MISC_PATH/0/conf/
|
||||
set_perm_recursive /data/misc/$MISC_PATH 0 0 0771 0660 "u:object_r:magisk_file:s0" || abortC "! ${LANG_CUST_ERR_PERM}"
|
||||
fi
|
||||
fi
|
||||
touch /data/adb/lspd/new_install || abortC "! ${LANG_CUST_ERR_CONF_FIRST}"
|
||||
ui_print "- ${LANG_CUST_INST_COPY_LIB}"
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package hidden;
|
||||
|
||||
import android.content.pm.PackageInstaller;
|
||||
import android.content.res.AssetManager;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.ResourcesImpl;
|
||||
|
|
@ -37,4 +38,12 @@ public class HiddenApiBridge {
|
|||
public static void Resources_setImpl(Resources resources, ResourcesImpl impl) {
|
||||
resources.setImpl(impl);
|
||||
}
|
||||
|
||||
public static int PackageInstaller_SessionParams_installFlags(PackageInstaller.SessionParams params) {
|
||||
return params.installFlags;
|
||||
}
|
||||
|
||||
public static void PackageInstaller_SessionParams_installFlags(PackageInstaller.SessionParams params, int flags) {
|
||||
params.installFlags = flags;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
package android.content;
|
||||
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.IInterface;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
public interface IIntentSender extends IInterface {
|
||||
|
||||
int send(int code, Intent intent, String resolvedType,
|
||||
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options);
|
||||
|
||||
@RequiresApi(26)
|
||||
void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
|
||||
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options);
|
||||
|
||||
abstract class Stub extends Binder implements IIntentSender {
|
||||
|
||||
public Stub() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public android.os.IBinder asBinder() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public static IIntentSender asInterface(IBinder binder) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package android.content;
|
||||
|
||||
public class IntentSender {
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package android.content.pm;
|
||||
|
||||
import android.content.IntentSender;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.IInterface;
|
||||
|
||||
public interface IPackageInstaller extends IInterface {
|
||||
|
||||
void uninstallExistingPackage(VersionedPackage versionedPackage, String callerPackageName,
|
||||
IntentSender statusReceiver, int userId);
|
||||
|
||||
abstract class Stub extends Binder implements IPackageInstaller {
|
||||
public static IPackageInstaller asInterface(IBinder obj) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -46,6 +46,8 @@ public interface IPackageManager extends IInterface {
|
|||
int checkUidPermission(String permName, int uid)
|
||||
throws RemoteException;
|
||||
|
||||
IPackageInstaller getPackageInstaller() throws RemoteException;
|
||||
|
||||
abstract class Stub extends Binder implements IPackageManager {
|
||||
|
||||
public static IPackageManager asInterface(IBinder obj) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
package android.content.pm;
|
||||
|
||||
public class PackageInstaller {
|
||||
public static class SessionParams {
|
||||
public int installFlags = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package android.content.pm;
|
||||
|
||||
public class VersionedPackage {
|
||||
}
|
||||
Loading…
Reference in New Issue