diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..3ba82c5e5 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: # replace +patreon: weishu +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: http://paypal.me/virtualxposed diff --git a/README.md b/README.md index d1d37b158..09f18544f 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ For Developers - [File a bug](https://github.com/android-hacker/exposed/issues) - [Wiki](https://github.com/android-hacker/VirtualXposed/wiki) +- [Telegram](https://t.me/vxp_devs) Credits ------- diff --git a/VirtualApp/app/build.gradle b/VirtualApp/app/build.gradle index 1442c9d3b..341de4ee2 100644 --- a/VirtualApp/app/build.gradle +++ b/VirtualApp/app/build.gradle @@ -20,11 +20,11 @@ android { compileSdkVersion 28 buildToolsVersion '28.0.3' defaultConfig { - applicationId "io.va.exposed" + applicationId "io.va.exposed64" minSdkVersion 21 targetSdkVersion 23 - versionCode 190 - versionName "0.19.0" + versionCode 220 + versionName "0.22.0" multiDexEnabled false android { defaultConfig { diff --git a/VirtualApp/app/src/main/AndroidManifest.xml b/VirtualApp/app/src/main/AndroidManifest.xml index d1f4b81b7..e56921e67 100644 --- a/VirtualApp/app/src/main/AndroidManifest.xml +++ b/VirtualApp/app/src/main/AndroidManifest.xml @@ -87,7 +87,7 @@ android:excludeFromRecents="true" android:label="@string/shared_to_vxp" android:screenOrientation="portrait" - android:taskAffinity="io.va.exposed.share" + android:taskAffinity="${applicationId}.share" android:theme="@style/Theme.AppCompat.Light.Dialog"> @@ -105,7 +105,7 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_installer_label" android:noHistory="true" - android:taskAffinity="io.va.exposed.installer" + android:taskAffinity="${applicationId}.installer" android:theme="@style/Theme.AppCompat.Light" /> - + diff --git a/VirtualApp/app/src/main/java/io/virtualapp/dev/CmdReceiver.java b/VirtualApp/app/src/main/java/io/virtualapp/dev/CmdReceiver.java index fc704a8ae..c296108f6 100644 --- a/VirtualApp/app/src/main/java/io/virtualapp/dev/CmdReceiver.java +++ b/VirtualApp/app/src/main/java/io/virtualapp/dev/CmdReceiver.java @@ -12,6 +12,7 @@ import com.lody.virtual.client.core.VirtualCore; import com.lody.virtual.remote.InstallResult; +import io.virtualapp.BuildConfig; import io.virtualapp.home.LoadingActivity; /** @@ -20,7 +21,7 @@ public class CmdReceiver extends BroadcastReceiver { - private static final String ACTION = "io.va.exposed.CMD"; + private static final String ACTION = BuildConfig.APPLICATION_ID + ".CMD"; private static final String KEY_CMD = "cmd"; private static final String KEY_PKG = "pkg"; private static final String KEY_UID = "uid"; diff --git a/VirtualApp/app/src/main/java/io/virtualapp/home/NewHomeActivity.java b/VirtualApp/app/src/main/java/io/virtualapp/home/NewHomeActivity.java index bfaf2824d..08bba4f37 100644 --- a/VirtualApp/app/src/main/java/io/virtualapp/home/NewHomeActivity.java +++ b/VirtualApp/app/src/main/java/io/virtualapp/home/NewHomeActivity.java @@ -40,6 +40,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicBoolean; import io.virtualapp.R; import io.virtualapp.abs.ui.VUiKit; @@ -62,7 +63,7 @@ public class NewHomeActivity extends NexusLauncherActivity { private Handler mUiHandler; private boolean mDirectlyBack = false; - private boolean checkXposedInstaller = true; + private final AtomicBoolean checkXposedInstaller = new AtomicBoolean(true); public static void goHome(Context context) { Intent intent = new Intent(context, NewHomeActivity.class); @@ -83,6 +84,9 @@ public void onCreate(Bundle savedInstanceState) { } private void installXposed() { + if (!VirtualCore.get().isXposedEnabled()) { + return; + } boolean isXposedInstalled = false; try { isXposedInstalled = VirtualCore.get().isAppInstalled(XPOSED_INSTALLER_PACKAGE); @@ -156,8 +160,7 @@ private static void dismissDialog(ProgressDialog dialog) { @Override protected void onResume() { super.onResume(); - if (checkXposedInstaller) { - checkXposedInstaller = false; + if (checkXposedInstaller.compareAndSet(true, false)) { installXposed(); } // check for update diff --git a/VirtualApp/app/src/main/java/io/virtualapp/home/adapters/CloneAppListAdapter.java b/VirtualApp/app/src/main/java/io/virtualapp/home/adapters/CloneAppListAdapter.java index 08b4848c5..5ff876be3 100644 --- a/VirtualApp/app/src/main/java/io/virtualapp/home/adapters/CloneAppListAdapter.java +++ b/VirtualApp/app/src/main/java/io/virtualapp/home/adapters/CloneAppListAdapter.java @@ -83,7 +83,7 @@ public void onBindViewHolder(ViewHolder holder, int position) { GlideUtils.loadPackageIconFromApkFile(mContext, info.path, holder.iconView, android.R.drawable.sym_def_app_icon); } - holder.nameView.setText(String.format("%s: %s", info.name, info.version)); + holder.nameView.setText(String.format("%s: %s%s", info.name, info.version, info.splitApk ? " [S]" : "")); if (isIndexSelected(position)) { holder.iconView.setAlpha(1f); holder.appCheckView.setImageResource(R.drawable.ic_check); diff --git a/VirtualApp/app/src/main/java/io/virtualapp/home/models/AppInfo.java b/VirtualApp/app/src/main/java/io/virtualapp/home/models/AppInfo.java index 65bffbe2b..0ae8230aa 100644 --- a/VirtualApp/app/src/main/java/io/virtualapp/home/models/AppInfo.java +++ b/VirtualApp/app/src/main/java/io/virtualapp/home/models/AppInfo.java @@ -15,4 +15,5 @@ public class AppInfo { public CharSequence version; public int cloneCount; public boolean disableMultiVersion; + public boolean splitApk; } diff --git a/VirtualApp/app/src/main/java/io/virtualapp/home/repo/AppRepository.java b/VirtualApp/app/src/main/java/io/virtualapp/home/repo/AppRepository.java index b918503d3..670b8c4be 100644 --- a/VirtualApp/app/src/main/java/io/virtualapp/home/repo/AppRepository.java +++ b/VirtualApp/app/src/main/java/io/virtualapp/home/repo/AppRepository.java @@ -181,6 +181,12 @@ private List convertPackageInfoToAppData(Context context, List convertPackageInfoToAppData(Context context, List intents); /** Deliver picture-in-picture mode change notification. */ public abstract void handlePictureInPictureModeChanged(IBinder token, boolean isInPipMode, Configuration overrideConfig); + + // Android 11 + public abstract void handlePictureInPictureRequested(IBinder token); + public abstract void handlePictureInPictureRequested(ActivityThread.ActivityClientRecord r); + + /** Signal to an activity (that is currently in PiP) of PiP state changes. */ + public abstract void handlePictureInPictureStateChanged(ActivityThread.ActivityClientRecord r, + Parcelable pipState); + + /** Whether the activity want to handle splash screen exit animation */ + public abstract boolean isHandleSplashScreenExit(IBinder token); + + /** Attach a splash screen window view to the top of the activity */ + public abstract void handleAttachSplashScreenView(ActivityThread.ActivityClientRecord r, + Parcelable parcelable); + + /** Hand over the splash screen window view to the activity */ + public abstract void handOverSplashScreenView(ActivityThread.ActivityClientRecord r); + /** Update window visibility. */ public abstract void handleWindowVisibility(IBinder token, boolean show); /** Perform activity launch. */ @@ -116,11 +175,44 @@ public abstract Activity handleLaunchActivity(ActivityThread.ActivityClientRecor /** Perform activity start. */ public abstract void handleStartActivity(ActivityThread.ActivityClientRecord r, PendingTransactionActions pendingActions); + + // Android 12 + /** Perform activity start. */ + public abstract void handleStartActivity(ActivityThread.ActivityClientRecord r, + PendingTransactionActions pendingActions, ActivityOptions options); + + // Android 11 + public abstract void handleStartActivity(IBinder binder, + PendingTransactionActions pendingActions); /** Get package info. */ public abstract LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, CompatibilityInfo compatInfo); /** Deliver app configuration change notification. */ public abstract void handleConfigurationChanged(Configuration config); + + public abstract void handleFixedRotationAdjustments(IBinder token, + DisplayAdjustments.FixedRotationAdjustments fixedRotationAdjustments); + + /** + * Add {@link ActivityThread.ActivityClientRecord} that is preparing to be launched. + * @param token Activity token. + * @param activity An initialized instance of {@link ActivityThread.ActivityClientRecord} to use during launch. + */ + public abstract void addLaunchingActivity(IBinder token, ActivityThread.ActivityClientRecord activity); + + /** + * Get {@link ActivityThread.ActivityClientRecord} that is preparing to be launched. + * @param token Activity token. + * @return An initialized instance of {@link ActivityThread.ActivityClientRecord} to use during launch. + */ + public abstract ActivityThread.ActivityClientRecord getLaunchingActivity(IBinder token); + + /** + * Remove {@link ActivityThread.ActivityClientRecord} from the launching activity list. + * @param token Activity token. + */ + public abstract void removeLaunchingActivity(IBinder token); + /** * Get {@link ActivityThread.ActivityClientRecord} instance that corresponds to the * provided token. @@ -166,6 +258,9 @@ public abstract void handleRelaunchActivity(ActivityThread.ActivityClientRecord public abstract void handleTopResumedActivityChanged(IBinder arg1, boolean arg2, String arg3); + public abstract void handleTopResumedActivityChanged(ActivityThread.ActivityClientRecord record, boolean arg2, String arg3); + + /** Count how many activities are launching. */ public abstract void countLaunchingActivities(int num); diff --git a/VirtualApp/lib/src/main/java/android/app/TransactionHandlerProxy.java b/VirtualApp/lib/src/main/java/android/app/TransactionHandlerProxy.java index 712b22cf0..51a592528 100644 --- a/VirtualApp/lib/src/main/java/android/app/TransactionHandlerProxy.java +++ b/VirtualApp/lib/src/main/java/android/app/TransactionHandlerProxy.java @@ -10,8 +10,10 @@ import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.os.IBinder; +import android.os.Parcelable; import android.util.Log; import android.util.MergedConfiguration; +import android.view.DisplayAdjustments; import com.lody.virtual.client.VClientImpl; import com.lody.virtual.client.core.VirtualCore; @@ -25,6 +27,7 @@ import mirror.android.app.ActivityManagerNative; import mirror.android.app.IActivityManager; +import mirror.com.android.internal.content.ReferrerIntent; /** @@ -71,16 +74,41 @@ public void handlePauseActivity(IBinder token, boolean finished, boolean userLea originalHandler.handlePauseActivity(token, finished, userLeaving, configChanges, pendingActions, reason); } + @Override + public void handleDestroyActivity(ActivityClientRecord r, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) { + originalHandler.handleDestroyActivity(r, finishing, configChanges, getNonConfigInstance, reason); + } + + @Override + public void handlePauseActivity(ActivityClientRecord r, boolean finished, boolean userLeaving, int configChanges, PendingTransactionActions pendingActions, String reason) { + originalHandler.handlePauseActivity(r, finished, userLeaving, configChanges, pendingActions, reason); + } + @Override public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) { originalHandler.handleResumeActivity(token, finalStateRequest, isForward, reason); } + @Override + public void handleResumeActivity(ActivityClientRecord record, boolean finalStateRequest, boolean isForward, String reason) { + originalHandler.handleResumeActivity(record, finalStateRequest, isForward, reason); + } + @Override public void handleStopActivity(IBinder token, boolean show, int configChanges, PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) { originalHandler.handleStopActivity(token, show, configChanges, pendingActions, finalStateRequest, reason); } + @Override + public void handleStopActivity(IBinder token, int configChanges, PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) { + originalHandler.handleStopActivity(token, configChanges, pendingActions, finalStateRequest, reason); + } + + @Override + public void handleStopActivity(ActivityClientRecord r, int configChanges, PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) { + originalHandler.handleStopActivity(r, configChanges, pendingActions, finalStateRequest, reason); + } + @Override public void reportStop(PendingTransactionActions pendingActions) { originalHandler.reportStop(pendingActions); @@ -91,16 +119,31 @@ public void performRestartActivity(IBinder token, boolean start) { originalHandler.performRestartActivity(token, start); } + @Override + public void performRestartActivity(ActivityClientRecord r, boolean start) { + originalHandler.performRestartActivity(r, start); + } + @Override public void handleActivityConfigurationChanged(IBinder activityToken, Configuration overrideConfig, int displayId) { originalHandler.handleActivityConfigurationChanged(activityToken, overrideConfig, displayId); } + @Override + public void handleActivityConfigurationChanged(ActivityClientRecord r, Configuration overrideConfig, int displayId) { + originalHandler.handleActivityConfigurationChanged(r, overrideConfig, displayId); + } + @Override public void handleSendResult(IBinder token, List results, String reason) { originalHandler.handleSendResult(token, results, reason); } + @Override + public void handleSendResult(ActivityClientRecord r, List results, String reason) { + originalHandler.handleSendResult(r, results, reason); + } + @Override public void handleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode, Configuration overrideConfig) { originalHandler.handleMultiWindowModeChanged(token, isInMultiWindowMode, overrideConfig); @@ -116,6 +159,36 @@ public void handlePictureInPictureModeChanged(IBinder token, boolean isInPipMode originalHandler.handlePictureInPictureModeChanged(token, isInPipMode, overrideConfig); } + @Override + public void handlePictureInPictureRequested(IBinder token) { + originalHandler.handlePictureInPictureRequested(token); + } + + @Override + public void handlePictureInPictureRequested(ActivityClientRecord r) { + originalHandler.handlePictureInPictureRequested(r); + } + + @Override + public void handlePictureInPictureStateChanged(ActivityClientRecord r, Parcelable pipState) { + originalHandler.handlePictureInPictureStateChanged(r, pipState); + } + + @Override + public boolean isHandleSplashScreenExit(IBinder token) { + return originalHandler.isHandleSplashScreenExit(token); + } + + @Override + public void handleAttachSplashScreenView(ActivityClientRecord r, Parcelable parcelable) { + originalHandler.handleAttachSplashScreenView(r, parcelable); + } + + @Override + public void handOverSplashScreenView(ActivityClientRecord r) { + originalHandler.handOverSplashScreenView(r); + } + @Override public void handleWindowVisibility(IBinder token, boolean show) { originalHandler.handleWindowVisibility(token, show); @@ -170,11 +243,21 @@ public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionA return originalHandler.handleLaunchActivity(r, pendingActions, customIntent); } + @Override + public void handleStartActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, ActivityOptions options) { + originalHandler.handleStartActivity(r, pendingActions, options); + } + @Override public void handleStartActivity(ActivityClientRecord r, PendingTransactionActions pendingActions) { originalHandler.handleStartActivity(r, pendingActions); } + @Override + public void handleStartActivity(IBinder binder, PendingTransactionActions pendingActions) { + originalHandler.handleStartActivity(binder, pendingActions); + } + @Override public LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, CompatibilityInfo compatInfo) { return originalHandler.getPackageInfoNoCheck(ai, compatInfo); @@ -185,6 +268,26 @@ public void handleConfigurationChanged(Configuration config) { originalHandler.handleConfigurationChanged(config); } + @Override + public void handleFixedRotationAdjustments(IBinder token, DisplayAdjustments.FixedRotationAdjustments fixedRotationAdjustments) { + originalHandler.handleFixedRotationAdjustments(token, fixedRotationAdjustments); + } + + @Override + public void addLaunchingActivity(IBinder token, ActivityClientRecord activity) { + originalHandler.addLaunchingActivity(token, activity); + } + + @Override + public ActivityClientRecord getLaunchingActivity(IBinder token) { + return originalHandler.getLaunchingActivity(token); + } + + @Override + public void removeLaunchingActivity(IBinder token) { + originalHandler.removeLaunchingActivity(token); + } + @Override public ActivityClientRecord getActivityClient(IBinder token) { Log.i(TAG, "getActivityClient : " + token); @@ -226,6 +329,11 @@ public void handleTopResumedActivityChanged(IBinder arg1, boolean arg2, String a originalHandler.handleTopResumedActivityChanged(arg1, arg2, arg3); } + @Override + public void handleTopResumedActivityChanged(ActivityClientRecord record, boolean arg2, String arg3) { + originalHandler.handleTopResumedActivityChanged(record, arg2, arg3); + } + @Override public void countLaunchingActivities(int num) { originalHandler.countLaunchingActivities(num); @@ -235,4 +343,10 @@ public void countLaunchingActivities(int num) { public void handleNewIntent(IBinder token, List intents) { originalHandler.handleNewIntent(token, intents); } + + @Override + public void handleNewIntent(ActivityClientRecord r, List intents) { + originalHandler.handleNewIntent(r, intents); + } + } diff --git a/VirtualApp/lib/src/main/java/android/content/pm/PackageParser.java b/VirtualApp/lib/src/main/java/android/content/pm/PackageParser.java index 92d180028..aa99d44a3 100644 --- a/VirtualApp/lib/src/main/java/android/content/pm/PackageParser.java +++ b/VirtualApp/lib/src/main/java/android/content/pm/PackageParser.java @@ -63,6 +63,13 @@ public class Package { // Applications requested features public ArrayList reqFeatures = null; public int mSharedUserLabel; + + public String[] splitNames; + public String codePath; + public String baseCodePath; + public String[] splitCodePaths; + + public ArrayList usesOptionalLibraries; } public final class Service extends Component { diff --git a/VirtualApp/lib/src/main/java/android/view/DisplayAdjustments.java b/VirtualApp/lib/src/main/java/android/view/DisplayAdjustments.java new file mode 100644 index 000000000..94d2da16d --- /dev/null +++ b/VirtualApp/lib/src/main/java/android/view/DisplayAdjustments.java @@ -0,0 +1,11 @@ +package android.view; + +/** + * @author weishu + * @date 2020/11/23. + */ + +// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/DisplayAdjustments.java;drc=master;l=184 +public class DisplayAdjustments { + public static class FixedRotationAdjustments {} +} diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/NativeEngine.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/NativeEngine.java index 719c1dedc..00fd2712b 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/NativeEngine.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/NativeEngine.java @@ -144,7 +144,7 @@ public static void forbid(String path) { public static void enableIORedirect() { try { - String soPath = VirtualCore.get().getContext().getApplicationInfo().nativeLibraryDir + File.separator + "lib" + LIB_NAME; + String soPath = VirtualCore.get().getContext().getApplicationInfo().nativeLibraryDir + File.separator + "lib" + LIB_NAME + ".so"; if (!new File(soPath).exists()) { throw new RuntimeException("io redirect failed."); } diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/VClientImpl.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/VClientImpl.java index 055721283..5afffe4a1 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/VClientImpl.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/VClientImpl.java @@ -44,6 +44,7 @@ import com.lody.virtual.client.stub.VASettings; import com.lody.virtual.helper.compat.BuildCompat; import com.lody.virtual.helper.compat.StorageManagerCompat; +import com.lody.virtual.helper.utils.Reflect; import com.lody.virtual.helper.utils.VLog; import com.lody.virtual.os.VEnvironment; import com.lody.virtual.os.VUserHandle; @@ -61,6 +62,7 @@ import java.util.Map; import java.util.Set; +import dalvik.system.DelegateLastClassLoader; import me.weishu.exposed.ExposedBridge; import mirror.android.app.ActivityThread; import mirror.android.app.ActivityThreadNMR1; @@ -70,6 +72,7 @@ import mirror.android.content.ContentProviderHolderOreo; import mirror.android.providers.Settings; import mirror.android.renderscript.RenderScriptCacheDir; +import mirror.android.security.net.config.ApplicationConfig; import mirror.android.view.HardwareRenderer; import mirror.android.view.RenderScript; import mirror.android.view.ThreadedRenderer; @@ -319,7 +322,7 @@ private void bindApplicationNoCheck(String packageName, String processName, Cond mirror.android.app.ActivityThread.AppBindData.info.set(boundApp, data.info); VMRuntime.setTargetSdkVersion.call(VMRuntime.getRuntime.call(), data.appInfo.targetSdkVersion); - boolean conflict = SpecialComponentList.isConflictingInstrumentation(packageName); + boolean conflict = SpecialComponentList.ConflictInstrumentation.isConflictingInstrumentation(packageName); if (!conflict) { InvocationStubManager.getInstance().checkEnv(AppInstrumentation.class); } @@ -344,12 +347,21 @@ private void bindApplicationNoCheck(String packageName, String processName, Cond VLog.w(TAG, "Xposed is disable.."); } + ClassLoader cl = LoadedApk.getClassLoader.call(data.info); + if (BuildCompat.isS()) { + ClassLoader parent = cl.getParent(); + Reflect.on(cl).set("parent", new DelegateLastClassLoader("/system/framework/android.test.base.jar", parent)); + } + + if (Build.VERSION.SDK_INT >= 30) + ApplicationConfig.setDefaultInstance.call(new Object[] { null }); mInitialApplication = LoadedApk.makeApplication.call(data.info, false, null); // ExposedBridge.patchAppClassLoader(context); mirror.android.app.ActivityThread.mInitialApplication.set(mainThread, mInitialApplication); ContextFixer.fixContext(mInitialApplication); + if (Build.VERSION.SDK_INT >= 24 && "com.tencent.mm:recovery".equals(processName)) { fixWeChatRecovery(mInitialApplication); } @@ -484,7 +496,8 @@ private void startIOUniformer() { private void setupVirtualStorage(ApplicationInfo info, int userId) { VirtualStorageManager vsManager = VirtualStorageManager.get(); boolean enable = vsManager.isVirtualStorageEnable(info.packageName, userId); - if (!enable) { + // Android 11, force enable storage redirect. + if (!enable && !(Build.VERSION.SDK_INT >= 30)) { // There are lots of situation to deal, I am tired, disable it now. // such as: FileProvider. return; @@ -507,7 +520,11 @@ private void setupVirtualStorage(ApplicationInfo info, int userId) { whiteList.add(Environment.DIRECTORY_MOVIES); whiteList.add(Environment.DIRECTORY_DOWNLOADS); whiteList.add(Environment.DIRECTORY_DCIM); - whiteList.add("Android/obb"); + // Android 11, do not tryna fetch this directory directly or crash. + // See docs below... + if (Build.VERSION.SDK_INT < 30) { + whiteList.add("Android/obb"); + } if (Build.VERSION.SDK_INT >= 19) { whiteList.add(Environment.DIRECTORY_DOCUMENTS); } @@ -535,8 +552,13 @@ private void setupVirtualStorage(ApplicationInfo info, int userId) { NativeEngine.whitelist(whitePath, true); } + // Android 11 -> see https://developer.android.com/training/data-storage#scoped-storage + // 安卓11 打开这个链接看看 https://developer.android.google.cn/training/data-storage#scoped-storage + // see https://android-opengrok.bangnimang.net/android-11.0.0_r8/xref/frameworks/base/core/java/android/os/Environment.java // redirect xxx/Android/data/ -> /xxx/Android/data//virtual/ NativeEngine.redirectDirectory(new File(storageRoot, "Android/data/").getAbsolutePath(), privatePath); + // redirect xxx/Android/obb/ -> /xxx/Android/data//virtual/ + NativeEngine.redirectDirectory(new File(storageRoot, "Android/obb/").getAbsolutePath(), privatePath); // redirect /sdcard/ -> vsdcard NativeEngine.redirectDirectory(storageRoot, vsPath); } @@ -547,6 +569,9 @@ private HashSet getMountPoints() { HashSet mountPoints = new HashSet<>(3); mountPoints.add("/mnt/sdcard/"); mountPoints.add("/sdcard/"); + // Redmi 10X Pro, Pixel 5... More mount points? + // 1@die.lu + mountPoints.add("/storage/self/primary/"); String[] points = StorageManagerCompat.getAllPoints(VirtualCore.get().getContext()); if (points != null) { Collections.addAll(mountPoints, points); diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/core/InvocationStubManager.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/core/InvocationStubManager.java index 0f7045161..cbed63796 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/core/InvocationStubManager.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/core/InvocationStubManager.java @@ -15,6 +15,7 @@ import com.lody.virtual.client.hook.proxies.appwidget.AppWidgetManagerStub; import com.lody.virtual.client.hook.proxies.audio.AudioManagerStub; import com.lody.virtual.client.hook.proxies.backup.BackupManagerStub; +import com.lody.virtual.client.hook.proxies.battery.BatteryStatsStub; import com.lody.virtual.client.hook.proxies.bluetooth.BluetoothStub; import com.lody.virtual.client.hook.proxies.clipboard.ClipBoardStub; import com.lody.virtual.client.hook.proxies.connectivity.ConnectivityStub; @@ -37,6 +38,7 @@ import com.lody.virtual.client.hook.proxies.mount.MountServiceStub; import com.lody.virtual.client.hook.proxies.network.NetworkManagementStub; import com.lody.virtual.client.hook.proxies.notification.NotificationManagerStub; +import com.lody.virtual.client.hook.proxies.os.DeviceIdentifiersPolicyServiceStub; import com.lody.virtual.client.hook.proxies.persistent_data_block.PersistentDataBlockServiceStub; import com.lody.virtual.client.hook.proxies.phonesubinfo.PhoneSubInfoStub; import com.lody.virtual.client.hook.proxies.pm.LauncherAppsStub; @@ -187,12 +189,17 @@ private void injectInternal() throws Throwable { addInjector(new WifiScannerStub()); addInjector(new ShortcutServiceStub()); addInjector(new DevicePolicyManagerStub()); + + addInjector(new BatteryStatsStub()); } if (BuildCompat.isOreo()) { addInjector(new AutoFillManagerStub()); } if (BuildCompat.isQ()) { addInjector(new ActivityTaskManagerStub()); + + // http://aospxref.com/android-10.0.0_r47/xref/frameworks/base/core/java/android/os/IDeviceIdentifiersPolicyService.aidl#24 + addInjector(new DeviceIdentifiersPolicyServiceStub()); } } } diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/core/VirtualCore.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/core/VirtualCore.java index 4cff92b84..339465545 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/core/VirtualCore.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/core/VirtualCore.java @@ -390,16 +390,6 @@ public void removeVisibleOutsidePackage(String pkg) { } public boolean isOutsidePackageVisible(String pkg) { - if (!isXposedEnabled()) { - PackageManager unHookPackageManager = getUnHookPackageManager(); - try { - unHookPackageManager.getPackageInfo(pkg, 0); - return true; - } catch (PackageManager.NameNotFoundException e) { - return false; - } - } - try { return getService().isOutsidePackageVisible(pkg); } catch (RemoteException e) { @@ -716,6 +706,11 @@ public Resources getResources(String pkg) throws Resources.NotFoundException { if (installedAppInfo != null) { AssetManager assets = mirror.android.content.res.AssetManager.ctor.newInstance(); mirror.android.content.res.AssetManager.addAssetPath.call(assets, installedAppInfo.apkPath); + if (installedAppInfo.splitCodePaths != null) { + for (String splitCodePath : installedAppInfo.splitCodePaths) { + mirror.android.content.res.AssetManager.addAssetPath.call(assets, splitCodePath); + } + } Resources hostRes = context.getResources(); return new Resources(assets, hostRes.getDisplayMetrics(), hostRes.getConfiguration()); } diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/env/SpecialComponentList.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/env/SpecialComponentList.java index 9d1872b22..96fa87142 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/env/SpecialComponentList.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/env/SpecialComponentList.java @@ -22,11 +22,47 @@ */ public final class SpecialComponentList { + public static class ConflictInstrumentation { + private static final HashSet INSTRUMENTATION_CONFLICTING = new HashSet<>(2); + + static { + INSTRUMENTATION_CONFLICTING.add("com.qihoo.magic"); + INSTRUMENTATION_CONFLICTING.add("com.qihoo.magic_mutiple"); + INSTRUMENTATION_CONFLICTING.add("com.facebook.katana"); + } + + public static boolean isConflictingInstrumentation(String packageName) { + return INSTRUMENTATION_CONFLICTING.contains(packageName); + } + } + + public static class SpecSystemComponent { + + private static final HashSet SPEC_SYSTEM_APP_LIST = new HashSet<>(3); + + static { + SPEC_SYSTEM_APP_LIST.add("android"); + SPEC_SYSTEM_APP_LIST.add("com.google.android.webview"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + try { + String webViewPkgN = IWebViewUpdateService.getCurrentWebViewPackageName.call(WebViewFactory.getUpdateService.call()); + if (webViewPkgN != null) { + SPEC_SYSTEM_APP_LIST.add(webViewPkgN); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + public static boolean isSpecSystemPackage(String pkg) { + return SPEC_SYSTEM_APP_LIST.contains(pkg); + } + } + private static final List ACTION_BLACK_LIST = new ArrayList(1); private static final Map PROTECTED_ACTION_MAP = new HashMap<>(5); private static final HashSet WHITE_PERMISSION = new HashSet<>(3); - private static final HashSet INSTRUMENTATION_CONFLICTING = new HashSet<>(2); - private static final HashSet SPEC_SYSTEM_APP_LIST = new HashSet<>(3); private static final Set SYSTEM_BROADCAST_ACTION = new HashSet<>(7); private static String PROTECT_ACTION_PREFIX = "_VA_protected_"; @@ -66,31 +102,9 @@ public final class SpecialComponentList { PROTECTED_ACTION_MAP.put("android.intent.action.USER_ADDED", Constants.ACTION_USER_ADDED); PROTECTED_ACTION_MAP.put("android.intent.action.USER_REMOVED", Constants.ACTION_USER_REMOVED); - INSTRUMENTATION_CONFLICTING.add("com.qihoo.magic"); - INSTRUMENTATION_CONFLICTING.add("com.qihoo.magic_mutiple"); - INSTRUMENTATION_CONFLICTING.add("com.facebook.katana"); - - SPEC_SYSTEM_APP_LIST.add("android"); - SPEC_SYSTEM_APP_LIST.add("com.google.android.webview"); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - try { - String webViewPkgN = IWebViewUpdateService.getCurrentWebViewPackageName.call(WebViewFactory.getUpdateService.call()); - if (webViewPkgN != null) { - SPEC_SYSTEM_APP_LIST.add(webViewPkgN); - } - } catch (Throwable e) { - e.printStackTrace(); - } - } } - public static boolean isSpecSystemPackage(String pkg) { - return SPEC_SYSTEM_APP_LIST.contains(pkg); - } - public static boolean isConflictingInstrumentation(String packageName) { - return INSTRUMENTATION_CONFLICTING.contains(packageName); - } /** * Check if the action in the BlackList. diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/fixer/ContextFixer.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/fixer/ContextFixer.java index 121d0d41a..bc50cbc37 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/fixer/ContextFixer.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/fixer/ContextFixer.java @@ -5,6 +5,7 @@ import android.os.Build; import android.os.DropBoxManager; +import com.lody.virtual.client.VClientImpl; import com.lody.virtual.client.core.InvocationStubManager; import com.lody.virtual.client.core.VirtualCore; import com.lody.virtual.client.hook.base.BinderInvocationStub; @@ -70,6 +71,26 @@ public static void fixContext(Context context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { ContentResolverJBMR2.mPackageName.set(context.getContentResolver(), hostPkg); } + + if (ContextImpl.getAttributionSource != null) { + fixAttributionSource(ContextImpl.getAttributionSource.call(context), hostPkg, VClientImpl.get().getVUid()); + } + } + + public static void fixAttributionSource(Object attr, String pkg, int uid) { + if (attr == null) { + return; + } + try { + Object mAttributionSourceState = Reflect.on(attr).get("mAttributionSourceState"); + Reflect.on(mAttributionSourceState).set("uid", uid); + Reflect.on(mAttributionSourceState).set("packageName", pkg); + + Object next = Reflect.on(attr).call("getNext").get(); + fixAttributionSource(next, pkg, uid); + } catch (Throwable e) { + e.printStackTrace(); + } } } diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/providers/ProviderHook.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/providers/ProviderHook.java index 2d20ae089..ffb032e04 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/providers/ProviderHook.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/providers/ProviderHook.java @@ -9,6 +9,8 @@ import android.os.IInterface; import android.os.ParcelFileDescriptor; +import com.lody.virtual.client.core.VirtualCore; +import com.lody.virtual.client.fixer.ContextFixer; import com.lody.virtual.client.hook.base.MethodBox; import com.lody.virtual.helper.compat.BuildCompat; import com.lody.virtual.helper.utils.VLog; @@ -153,11 +155,26 @@ public Object invoke(Object proxy, Method method, Object... args) throws Throwab } MethodBox methodBox = new MethodBox(method, mBase, args); int start = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 ? 1 : 0; - + if (BuildCompat.isS()) { + // https://cs.android.com/android/platform/superproject/+/android-12.0.0_r16:frameworks/base/core/java/android/content/IContentProvider.java + start = 1; + } else if (BuildCompat.isR()) { + // Android 11: https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/content/IContentProvider.java?q=IContentProvider&ss=android%2Fplatform%2Fsuperproject + start = 2; + } + if (BuildCompat.isS() ) { + tryFixAttributionSource(args); + } try { String name = method.getName(); if ("call".equals(name)) { - if (BuildCompat.isQ()) { + if (BuildCompat.isS()) { + // What's the fuck with Google??? + // https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/core/java/android/content/IContentProvider.java;l=123 + start = 2; + } else if (BuildCompat.isR()) { + start = 3; + } else if (BuildCompat.isQ()) { start = 2; } String methodName = (String) args[start]; @@ -200,6 +217,7 @@ public Object invoke(Object proxy, Method method, Object... args) throws Throwab String[] selectionArgs = null; String sortOrder = null; Bundle queryArgs = null; + if (Build.VERSION.SDK_INT >= 26) { queryArgs = (Bundle) args[start + 2]; if (queryArgs != null) { @@ -231,4 +249,19 @@ protected void processArgs(Method method, Object... args) { public interface HookFetcher { ProviderHook fetch(boolean external, IInterface provider); } + + private void tryFixAttributionSource(Object[] args) { + if (args == null || args.length == 0) { + return; + } + Object attribution = args[0]; + if (attribution == null) { + return; + } + if (!attribution.getClass().getName().equals("android.content.AttributionSource")) { + return; + } + + ContextFixer.fixAttributionSource(attribution, VirtualCore.get().getHostPkg(), VirtualCore.get().myUid()); + } } diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/account/AccountManagerStub.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/account/AccountManagerStub.java index 144630917..455be4e25 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/account/AccountManagerStub.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/account/AccountManagerStub.java @@ -62,6 +62,8 @@ protected void onBindMethods() { addMethodProxy(new renameAccount()); addMethodProxy(new getPreviousName()); addMethodProxy(new renameSharedAccountAsUser()); + + addMethodProxy(new setAccountVisibility()); } private static class getPassword extends MethodProxy { @@ -594,4 +596,17 @@ public Object call(Object who, Method method, Object... args) throws Throwable { return method.invoke(who, args); } } + + private static class setAccountVisibility extends MethodProxy { + + @Override + public String getMethodName() { + return "setAccountVisibility"; + } + + @Override + public Object call(Object who, Method method, Object... args) throws Throwable { + return true; + } + } } diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/am/ActivityTaskManagerStub.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/am/ActivityTaskManagerStub.java index 1aba2d190..d858da006 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/am/ActivityTaskManagerStub.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/am/ActivityTaskManagerStub.java @@ -4,8 +4,10 @@ import com.lody.virtual.client.hook.base.BinderInvocationProxy; import com.lody.virtual.client.hook.base.Inject; +import com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy; import com.lody.virtual.client.hook.base.StaticMethodProxy; import com.lody.virtual.client.ipc.VActivityManager; +import com.lody.virtual.helper.compat.BuildCompat; import java.lang.reflect.Method; @@ -49,5 +51,9 @@ public Object call(Object who, Method method, Object... args) throws Throwable { return super.call(who, method, args); } }); + + if (BuildCompat.isQ()) { + addMethodProxy(new ReplaceCallingPkgMethodProxy("getAppTasks")); + } } } diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/am/MethodProxies.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/am/MethodProxies.java index 1d42d0962..a3ce5ff36 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/am/MethodProxies.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/am/MethodProxies.java @@ -289,6 +289,10 @@ public boolean isEnable() { static class GetIntentSender extends MethodProxy { + protected int mIntentIndex = 5; + protected int mResolvedTypesIndex = 6; + protected int mFlagsIndex = 7; + @Override public String getMethodName() { return "getIntentSender"; @@ -297,11 +301,11 @@ public String getMethodName() { @Override public Object call(Object who, Method method, Object... args) throws Throwable { String creator = (String) args[1]; - String[] resolvedTypes = (String[]) args[6]; + String[] resolvedTypes = (String[]) args[mResolvedTypesIndex]; int type = (int) args[0]; - int flags = (int) args[7]; + int flags = (int) args[mFlagsIndex]; if (args[5] instanceof Intent[]) { - Intent[] intents = (Intent[]) args[5]; + Intent[] intents = (Intent[]) args[mIntentIndex]; for (int i = 0; i < intents.length; i++) { Intent intent = intents[i]; if (resolvedTypes != null && i < resolvedTypes.length) { @@ -313,7 +317,7 @@ public Object call(Object who, Method method, Object... args) throws Throwable { } } } - args[7] = flags; + args[mFlagsIndex] = flags; args[1] = getHostPkg(); // Force userId to 0 if (args[args.length - 1] instanceof Integer) { @@ -459,6 +463,11 @@ public Object call(Object who, Method method, Object... args) throws Throwable { if (intent.getPackage() != null && isAppPkg(intent.getPackage())) { return ActivityManagerCompat.START_INTENT_NOT_RESOLVED; } + + if (Build.VERSION.SDK_INT >= 30) { + args[1] = VirtualCore.get().getContext().getPackageName(); + } + return method.invoke(who, args); } int res = VActivityManager.get().startActivity(intent, activityInfo, resultTo, options, resultWho, requestCode, VUserHandle.myUserId()); @@ -1228,14 +1237,14 @@ public boolean isEnable() { static class RegisterReceiver extends MethodProxy { - private static final int IDX_IIntentReceiver = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1 + protected int mIIntentReceiverIndex = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1 ? 2 : 1; - private static final int IDX_RequiredPermission = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1 + protected int mRequiredPermissionIndex = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1 ? 4 : 3; - private static final int IDX_IntentFilter = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1 + protected int mIntentFilterIndex = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1 ? 3 : 2; @@ -1249,11 +1258,11 @@ public String getMethodName() { @Override public Object call(Object who, Method method, Object... args) throws Throwable { MethodParameterUtils.replaceFirstAppPkg(args); - args[IDX_RequiredPermission] = null; - IntentFilter filter = (IntentFilter) args[IDX_IntentFilter]; + args[mRequiredPermissionIndex] = null; + IntentFilter filter = (IntentFilter) args[mIntentFilterIndex]; SpecialComponentList.protectIntentFilter(filter); - if (args.length > IDX_IIntentReceiver && IIntentReceiver.class.isInstance(args[IDX_IIntentReceiver])) { - final IInterface old = (IInterface) args[IDX_IIntentReceiver]; + if (args.length > mIIntentReceiverIndex && IIntentReceiver.class.isInstance(args[mIIntentReceiverIndex])) { + final IInterface old = (IInterface) args[mIIntentReceiverIndex]; if (!IIntentReceiverProxy.class.isInstance(old)) { final IBinder token = old.asBinder(); if (token != null) { @@ -1272,7 +1281,7 @@ public void binderDied() { WeakReference mDispatcher = LoadedApk.ReceiverDispatcher.InnerReceiver.mDispatcher.get(old); if (mDispatcher != null) { LoadedApk.ReceiverDispatcher.mIIntentReceiver.set(mDispatcher.get(), proxyIIntentReceiver); - args[IDX_IIntentReceiver] = proxyIIntentReceiver; + args[mIIntentReceiverIndex] = proxyIIntentReceiver; } } } @@ -1382,6 +1391,11 @@ public Object call(Object who, Method method, Object... args) throws Throwable { return null; } args[nameIdx] = VASettings.getStubAuthority(targetVPid); + + if (Build.VERSION.SDK_INT >= 30) { + args[1] = VirtualCore.get().getContext().getPackageName(); + } + Object holder = method.invoke(who, args); if (holder == null) { return null; @@ -1762,4 +1776,41 @@ public GetPackageProcessState() { super("getPackageProcessState"); } } + + // For Android 11 + static class RegisterReceiverWithFeature extends RegisterReceiver { + public RegisterReceiverWithFeature() { + if (BuildCompat.isS()) { + // http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/core/java/android/app/IActivityManager.aidl?fi=IActivityManager#127 + mIIntentReceiverIndex = 4; + mIntentFilterIndex = 5; + mRequiredPermissionIndex = 6; + } else { + // http://aospxref.com/android-11.0.0_r21/xref/frameworks/base/core/java/android/app/IActivityManager.aidl?fi=IActivityManager#124 + mIIntentReceiverIndex = 3; + mIntentFilterIndex = 4; + mRequiredPermissionIndex = 5; + } + } + + @Override + public String getMethodName() { + return "registerReceiverWithFeature"; + } + } + + static class GetIntentSenderWithFeature extends GetIntentSender { + + public GetIntentSenderWithFeature() { + // http://aospxref.com/android-11.0.0_r21/xref/frameworks/base/core/java/android/app/IActivityManager.aidl?fi=IActivityManager#245 + mIntentIndex = 6; + mResolvedTypesIndex = 7; + mFlagsIndex = 8; + } + + @Override + public String getMethodName() { + return "getIntentSenderWithFeature"; + } + } } diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/battery/BatteryStatsStub.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/battery/BatteryStatsStub.java new file mode 100644 index 000000000..bae215ad3 --- /dev/null +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/battery/BatteryStatsStub.java @@ -0,0 +1,33 @@ +package com.lody.virtual.client.hook.proxies.battery; + +import android.content.Context; +import android.os.IInterface; + +import com.lody.virtual.client.core.VirtualCore; +import com.lody.virtual.client.hook.base.BinderInvocationProxy; +import com.lody.virtual.client.hook.base.ReplaceLastUidMethodProxy; + +import mirror.com.android.internal.os.health.SystemHealthManager; + +/** + * @author weishu + * @date 2020/11/24. + */ +public class BatteryStatsStub extends BinderInvocationProxy { + + public BatteryStatsStub() { + super(getInterface(), "batterystats"); + } + + private static IInterface getInterface() { + Object manager = VirtualCore.get().getContext().getSystemService(Context.SYSTEM_HEALTH_SERVICE); + return SystemHealthManager.mBatteryStats.get(manager); + } + + @Override + protected void onBindMethods() { + super.onBindMethods(); + + addMethodProxy(new ReplaceLastUidMethodProxy("takeUidSnapshot")); + } +} diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/location/MethodProxies.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/location/MethodProxies.java index 7aa156b8e..8199cbe3a 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/location/MethodProxies.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/location/MethodProxies.java @@ -4,13 +4,14 @@ import android.location.LocationRequest; import android.os.Build; +import com.lody.virtual.client.core.VirtualCore; import com.lody.virtual.client.hook.base.MethodProxy; import com.lody.virtual.client.hook.base.ReplaceLastPkgMethodProxy; import com.lody.virtual.client.ipc.VirtualLocationManager; import com.lody.virtual.helper.utils.ArrayUtils; import com.lody.virtual.helper.utils.Reflect; import com.lody.virtual.remote.vloc.VLocation; - +import com.lody.virtual.client.hook.utils.MethodParameterUtils; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; @@ -84,6 +85,11 @@ public Object call(final Object who, Method method, Object... args) throws Throw } return 0; } + + if (Build.VERSION.SDK_INT >= 30) { + args[3] = VirtualCore.get().getContext().getPackageName(); + } + return super.call(who, method, args); } } @@ -254,6 +260,7 @@ public String getMethodName() { @Override public Object call(Object who, Method method, Object... args) throws Throwable { + MethodParameterUtils.replaceFirstAppPkg(args); if (!isFakeLocationEnable()) { return super.call(who, method, args); } diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/notification/MethodProxies.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/notification/MethodProxies.java index f4975a391..ce88f6fd3 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/notification/MethodProxies.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/notification/MethodProxies.java @@ -105,13 +105,23 @@ public Object call(Object who, Method method, Object... args) throws Throwable { if (getHostPkg().equals(pkg)) { return method.invoke(who, args); } - String tag = (String) args[1]; - int id = (int) args[2]; + + int index_tag = 1; + int index_id = 2; + + if (Build.VERSION.SDK_INT >= 30) { + index_tag = 2; + index_id = 3; + } + + String tag = (String) args[index_tag]; + int id = (int) args[index_id]; + id = VNotificationManager.get().dealNotificationId(id, pkg, tag, getAppUserId()); tag = VNotificationManager.get().dealNotificationTag(id, pkg, tag, getAppUserId()); - args[1] = tag; - args[2] = id; + args[index_tag] = tag; + args[index_id] = id; return method.invoke(who, args); } } diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/os/DeviceIdentifiersPolicyServiceStub.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/os/DeviceIdentifiersPolicyServiceStub.java new file mode 100644 index 000000000..3baf90928 --- /dev/null +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/os/DeviceIdentifiersPolicyServiceStub.java @@ -0,0 +1,37 @@ +package com.lody.virtual.client.hook.proxies.os; + +import android.content.pm.ApplicationInfo; + +import com.lody.virtual.client.VClientImpl; +import com.lody.virtual.client.hook.base.BinderInvocationProxy; +import com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy; +import com.lody.virtual.helper.compat.BuildCompat; + +import java.lang.reflect.Method; + +import mirror.android.os.IDeviceIdentifiersPolicyService; + +/** + * @author weishu + * @date 2021/8/19. + */ +public class DeviceIdentifiersPolicyServiceStub extends BinderInvocationProxy { + public DeviceIdentifiersPolicyServiceStub() { + super(IDeviceIdentifiersPolicyService.Stub.TYPE, "device_identifiers"); + } + + @Override + protected void onBindMethods() { + + addMethodProxy(new ReplaceCallingPkgMethodProxy("getSerialForPackage") { + @Override + public Object call(Object who, Method method, Object... args) throws Throwable { + ApplicationInfo info = VClientImpl.get().getCurrentApplicationInfo(); + if (info != null && info.targetSdkVersion >= 29 && BuildCompat.isQ()) { + return "unknown"; + } + return super.call(who, method, args); + } + }); + } +} diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/telephony/TelephonyRegistryStub.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/telephony/TelephonyRegistryStub.java index e0dbe2a86..25c14852a 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/telephony/TelephonyRegistryStub.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/telephony/TelephonyRegistryStub.java @@ -5,6 +5,7 @@ import com.lody.virtual.client.hook.base.BinderInvocationProxy; import com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy; import com.lody.virtual.client.hook.base.ReplaceSequencePkgMethodProxy; +import com.lody.virtual.helper.compat.BuildCompat; import java.lang.reflect.Method; @@ -39,5 +40,9 @@ public boolean beforeCall(Object who, Method method, Object... args) { return super.beforeCall(who, method, args); } }); + + if (BuildCompat.isS()) { + addMethodProxy(new ReplaceCallingPkgMethodProxy("listenWithEventList")); + } } } diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/telephony/TelephonyStub.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/telephony/TelephonyStub.java index ebdbbef34..bde03c6a3 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/telephony/TelephonyStub.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/telephony/TelephonyStub.java @@ -1,12 +1,20 @@ package com.lody.virtual.client.hook.proxies.telephony; +import android.Manifest; import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.os.Build; -import com.lody.virtual.client.hook.base.Inject; +import com.lody.virtual.client.VClientImpl; +import com.lody.virtual.client.core.VirtualCore; import com.lody.virtual.client.hook.base.BinderInvocationProxy; +import com.lody.virtual.client.hook.base.Inject; import com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy; import com.lody.virtual.client.hook.base.ReplaceLastPkgMethodProxy; +import java.lang.reflect.Method; + import mirror.com.android.internal.telephony.ITelephony; /** @@ -51,5 +59,28 @@ protected void onBindMethods() { addMethodProxy(new ReplaceCallingPkgMethodProxy("getMergedSubscriberIds")); addMethodProxy(new ReplaceLastPkgMethodProxy("getRadioAccessFamily")); addMethodProxy(new ReplaceCallingPkgMethodProxy("isVideoCallingEnabled")); + + addMethodProxy(new ReplaceCallingPkgMethodProxy("getDeviceIdWithFeature") { + @Override + public Object call(Object who, Method method, Object... args) throws Throwable{ + try { + return super.call(who, method, args); + } catch (SecurityException e) { + ApplicationInfo ai = VClientImpl.get().getCurrentApplicationInfo(); + if (ai.targetSdkVersion >= 29) { + throw e; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + Context context = VirtualCore.get().getContext(); + if (context.checkSelfPermission(Manifest.permission.READ_PHONE_STATE) + != PackageManager.PERMISSION_GRANTED) { + // 不排除不检查权限直接使用 try-catch 判断的情况 + throw e; + } + } + return null; + } + } + }); } } diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/window/session/WindowSessionPatch.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/window/session/WindowSessionPatch.java index 1ad50fa74..ed0ff452d 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/window/session/WindowSessionPatch.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/window/session/WindowSessionPatch.java @@ -1,10 +1,10 @@ package com.lody.virtual.client.hook.proxies.window.session; +import android.os.Build; import android.os.IInterface; import com.lody.virtual.client.hook.base.MethodInvocationProxy; import com.lody.virtual.client.hook.base.MethodInvocationStub; -import com.lody.virtual.client.hook.base.ReplaceCallingPkgMethodProxy; /** * @author Lody @@ -22,6 +22,12 @@ public void onBindMethods() { addMethodProxy(new BaseMethodProxy("addToDisplayWithoutInputChannel")); addMethodProxy(new BaseMethodProxy("addWithoutInputChannel")); addMethodProxy(new BaseMethodProxy("relayout")); + + // http://aospxref.com/android-11.0.0_r21/xref/frameworks/base/core/java/android/view/IWindowSession.aidl#51 + if (Build.VERSION.SDK_INT >= 30) { + addMethodProxy(new BaseMethodProxy("addToDisplayAsUser")); + addMethodProxy(new BaseMethodProxy("grantInputChannel")); + } } diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VPackageManager.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VPackageManager.java index 1f46d97a0..e647b75b9 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VPackageManager.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VPackageManager.java @@ -15,6 +15,7 @@ import com.lody.virtual.client.core.VirtualCore; import com.lody.virtual.client.env.VirtualRuntime; +import com.lody.virtual.helper.compat.BuildCompat; import com.lody.virtual.server.IPackageInstaller; import com.lody.virtual.server.IPackageManager; @@ -189,7 +190,11 @@ public ApplicationInfo getApplicationInfo(String packageName, int flags, int use if (!new File(APACHE_LEGACY).exists()) { APACHE_LEGACY = "/system/framework/org.apache.http.legacy.jar"; } - if (android.os.Build.VERSION.SDK_INT >= P && info.targetSdkVersion <= P) { + List sharedLibraries = getInterface().getSharedLibraries(packageName); + boolean forceAdd = sharedLibraries.contains("org.apache.http.legacy"); + + // https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdater.java;l=36?q=OrgApacheHttpLegacyUpdater&ss=android%2Fplatform%2Fsuperproject:frameworks%2Fbase%2Fservices%2Fcore%2Fjava%2Fcom%2Fandroid%2Fserver%2Fpm%2Fparsing%2Flibrary%2F + if (android.os.Build.VERSION.SDK_INT >= P && info.targetSdkVersion < P || forceAdd) { String[] newSharedLibraryFiles; if (info.sharedLibraryFiles == null) { newSharedLibraryFiles = new String[]{APACHE_LEGACY}; @@ -201,6 +206,20 @@ public ApplicationInfo getApplicationInfo(String packageName, int flags, int use } info.sharedLibraryFiles = newSharedLibraryFiles; } + + String TEST_BASE = "/system/framework/android.test.base.jar"; + if (BuildCompat.isR() && new File(TEST_BASE).exists()) { + String[] newSharedLibraryFiles; + if (info.sharedLibraryFiles == null) { + newSharedLibraryFiles = new String[]{TEST_BASE}; + } else { + int newLength = info.sharedLibraryFiles.length + 1; + newSharedLibraryFiles = new String[newLength]; + System.arraycopy(info.sharedLibraryFiles, 0, newSharedLibraryFiles, 0, newLength - 1); + newSharedLibraryFiles[newLength - 1] = TEST_BASE; + } + info.sharedLibraryFiles = newSharedLibraryFiles; + } return info; } catch (RemoteException e) { return VirtualRuntime.crash(e); diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/helper/compat/BuildCompat.java b/VirtualApp/lib/src/main/java/com/lody/virtual/helper/compat/BuildCompat.java index 02f3ad0eb..b788dbe5a 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/helper/compat/BuildCompat.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/helper/compat/BuildCompat.java @@ -20,15 +20,31 @@ public static int getPreviewSDKInt() { } public static boolean isOreo() { + return isAndroidLevel(Build.VERSION_CODES.O); + } - return (Build.VERSION.SDK_INT == 25 && getPreviewSDKInt() > 0) - || Build.VERSION.SDK_INT > 25; + public static boolean isPie() { + return isAndroidLevel(Build.VERSION_CODES.P); } public static boolean isQ() { - final int SDK = Build.VERSION.SDK_INT; - final int Q = 29; - return SDK == Q - 1 && getPreviewSDKInt() > 0 || Build.VERSION.SDK_INT >= Q; + return isAndroidLevel(29); + } + + public static boolean isR() { + return isAndroidLevel(30); + } + + public static boolean isS() { + return isAndroidLevel(31); + } + + private static boolean isAndroidLevelPreview(int level) { + return (Build.VERSION.SDK_INT == level && getPreviewSDKInt() > 0) + || Build.VERSION.SDK_INT > level; } + private static boolean isAndroidLevel(int level) { + return Build.VERSION.SDK_INT >= level; + } } \ No newline at end of file diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/helper/utils/ComponentUtils.java b/VirtualApp/lib/src/main/java/com/lody/virtual/helper/utils/ComponentUtils.java index 520b64030..ef32f9d42 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/helper/utils/ComponentUtils.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/helper/utils/ComponentUtils.java @@ -96,7 +96,7 @@ public static ComponentName toComponentName(ComponentInfo componentInfo) { public static boolean isSystemApp(ApplicationInfo applicationInfo) { return !GmsSupport.isGmsFamilyPackage(applicationInfo.packageName) && ((ApplicationInfo.FLAG_SYSTEM & applicationInfo.flags) != 0 - || SpecialComponentList.isSpecSystemPackage(applicationInfo.packageName)); + || SpecialComponentList.SpecSystemComponent.isSpecSystemPackage(applicationInfo.packageName)); } public static boolean isStubComponent(Intent intent) { diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/remote/InstalledAppInfo.java b/VirtualApp/lib/src/main/java/com/lody/virtual/remote/InstalledAppInfo.java index d93da4932..675de4521 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/remote/InstalledAppInfo.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/remote/InstalledAppInfo.java @@ -21,13 +21,15 @@ public final class InstalledAppInfo implements Parcelable { public String libPath; public boolean dependSystem; public int appId; + public String[] splitCodePaths; - public InstalledAppInfo(String packageName, String apkPath, String libPath, boolean dependSystem, boolean skipDexOpt, int appId) { + public InstalledAppInfo(String packageName, String apkPath, String libPath, boolean dependSystem, boolean skipDexOpt, int appId, String[] splitCodePaths) { this.packageName = packageName; this.apkPath = apkPath; this.libPath = libPath; this.dependSystem = dependSystem; this.appId = appId; + this.splitCodePaths = splitCodePaths; } public File getOdexFile() { @@ -62,6 +64,8 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeString(this.libPath); dest.writeByte(this.dependSystem ? (byte) 1 : (byte) 0); dest.writeInt(this.appId); + + dest.writeStringArray(this.splitCodePaths); } protected InstalledAppInfo(Parcel in) { @@ -70,6 +74,8 @@ protected InstalledAppInfo(Parcel in) { this.libPath = in.readString(); this.dependSystem = in.readByte() != 0; this.appId = in.readInt(); + + this.splitCodePaths = in.createStringArray(); } public static final Creator CREATOR = new Creator() { diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/PackageSetting.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/PackageSetting.java index f3a049b2f..ec0780f1f 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/PackageSetting.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/PackageSetting.java @@ -33,6 +33,8 @@ public PackageSetting[] newArray(int size) { public int appId; public long firstInstallTime; public long lastUpdateTime; + + public String[] splitCodePaths; private SparseArray userState = new SparseArray<>(); public PackageSetting() { @@ -47,10 +49,11 @@ protected PackageSetting(Parcel in) { //noinspection unchecked this.userState = in.readSparseArray(PackageUserState.class.getClassLoader()); this.skipDexOpt = in.readByte() != 0; + this.splitCodePaths = in.createStringArray(); } public InstalledAppInfo getAppInfo() { - return new InstalledAppInfo(packageName, apkPath, libPath, dependSystem, skipDexOpt, appId); + return new InstalledAppInfo(packageName, apkPath, libPath, dependSystem, skipDexOpt, appId, splitCodePaths); } PackageUserState modifyUserState(int userId) { @@ -96,6 +99,7 @@ public void writeToParcel(Parcel dest, int flags) { //noinspection unchecked dest.writeSparseArray((SparseArray) this.userState); dest.writeByte(this.skipDexOpt ? (byte) 1 : (byte) 0); + dest.writeStringArray(this.splitCodePaths); } public boolean isLaunched(int userId) { diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VAppManagerService.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VAppManagerService.java index aa851f9cd..c51929047 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VAppManagerService.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VAppManagerService.java @@ -8,8 +8,6 @@ import com.lody.virtual.client.core.InstallStrategy; import com.lody.virtual.client.core.VirtualCore; -import com.lody.virtual.client.env.VirtualRuntime; -import com.lody.virtual.helper.ArtDexOptimizer; import com.lody.virtual.helper.collection.IntArray; import com.lody.virtual.helper.compat.NativeLibraryHelperCompat; import com.lody.virtual.helper.utils.ArrayUtils; @@ -37,8 +35,6 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicReference; -import dalvik.system.DexFile; - /** * @author Lody */ @@ -151,7 +147,7 @@ public synchronized InstallResult installPackage(String path, int flags, boolean return InstallResult.makeFailure("path = NULL"); } File packageFile = new File(path); - if (!packageFile.exists() || !packageFile.isFile()) { + if (!packageFile.exists()) { return InstallResult.makeFailure("Package File is not exist."); } VPackage pkg = null; @@ -195,7 +191,8 @@ public synchronized InstallResult installPackage(String path, int flags, boolean dependSystem = false; } - NativeLibraryHelperCompat.copyNativeBinaries(new File(path), libDir); + String[] splitCodePaths = null; + if (!dependSystem) { File privatePackageFile = new File(appDir, "base.apk"); File parentFolder = privatePackageFile.getParentFile(); @@ -204,14 +201,39 @@ public synchronized InstallResult installPackage(String path, int flags, boolean } else if (privatePackageFile.exists() && !privatePackageFile.delete()) { VLog.w(TAG, "Warning: unable to delete file : " + privatePackageFile.getPath()); } + File baseApkFile = packageFile.isFile() ? packageFile : new File(pkg.baseCodePath); try { - FileUtils.copyFile(packageFile, privatePackageFile); + FileUtils.copyFile(baseApkFile, privatePackageFile); } catch (IOException e) { privatePackageFile.delete(); return InstallResult.makeFailure("Unable to copy the package file."); } + // copy lib in base apk + NativeLibraryHelperCompat.copyNativeBinaries(baseApkFile, libDir); + packageFile = privatePackageFile; + + if (pkg.splitNames != null) { + int length = pkg.splitNames.length; + splitCodePaths = new String[length]; + + for (int i = 0; i < length; i++) { + String splitName = pkg.splitNames[i]; + File privateSplitFile = new File(appDir, splitName + ".apk"); + try { + FileUtils.copyFile(new File(pkg.splitCodePaths[i]), privateSplitFile); + + // copy lib in split apk + NativeLibraryHelperCompat.copyNativeBinaries(privateSplitFile, libDir); + } catch (IOException e) { + privateSplitFile.delete(); + return InstallResult.makeFailure("Unable to copy split: " + splitName); + } + splitCodePaths[i] = privateSplitFile.getPath(); + } + } } + if (existOne != null) { PackageCacheManager.remove(pkg.packageName); } @@ -237,29 +259,10 @@ public synchronized InstallResult installPackage(String path, int flags, boolean ps.setUserState(userId, false/*launched*/, false/*hidden*/, installed); } } + ps.splitCodePaths = splitCodePaths; PackageParserEx.savePackageCache(pkg); PackageCacheManager.put(pkg, ps); mPersistenceLayer.save(); - if (!dependSystem) { - boolean runDexOpt = false; - if (VirtualRuntime.isArt()) { - try { - ArtDexOptimizer.compileDex2Oat(ps.apkPath, VEnvironment.getOdexFile(ps.packageName).getPath()); - } catch (IOException e) { - e.printStackTrace(); - runDexOpt = true; - } - } else { - runDexOpt = true; - } - if (runDexOpt) { - try { - DexFile.loadDex(ps.apkPath, VEnvironment.getOdexFile(ps.packageName).getPath(), 0).close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } BroadcastSystem.get().startApp(pkg); if (notify) { notifyAppInstalled(ps, -1); diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VPackageManagerService.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VPackageManagerService.java index 4c5a1f7af..a7fa9f849 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VPackageManagerService.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/VPackageManagerService.java @@ -220,7 +220,14 @@ public List getSharedLibraries(String packageName) { synchronized (mPackages) { VPackage p = mPackages.get(packageName); if (p != null) { - return p.usesLibraries; + ArrayList list = new ArrayList<>(); + if (p.usesLibraries != null) { + list.addAll(p.usesLibraries); + } + if (p.usesOptionalLibraries != null) { + list.addAll(p.usesOptionalLibraries); + } + return list; } return null; } @@ -253,7 +260,11 @@ private PackageInfo generatePackageInfo(VPackage p, PackageSetting ps, int flags PackageInfo packageInfo = PackageParserEx.generatePackageInfo(p, flags, ps.firstInstallTime, ps.lastUpdateTime, ps.readUserState(userId), userId); if (packageInfo != null) { - return packageInfo; + Parcel parcel = Parcel.obtain(); + packageInfo.writeToParcel(parcel, 0); + PackageInfo info = PackageInfo.CREATOR.createFromParcel(parcel); + parcel.recycle(); + return info; } return null; } diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/installer/PackageInstallerSession.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/installer/PackageInstallerSession.java index 2228124e3..d37c9a104 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/installer/PackageInstallerSession.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/installer/PackageInstallerSession.java @@ -20,6 +20,8 @@ import com.lody.virtual.helper.utils.FileUtils; import com.lody.virtual.helper.utils.VLog; +import com.lody.virtual.remote.InstallResult; +import com.lody.virtual.server.pm.VAppManagerService; import java.io.File; import java.io.FileDescriptor; @@ -179,6 +181,10 @@ public void onPackageInstalled(String basePackageName, int returnCode, String ms dispatchSessionFinished(returnCode, msg, extras); } }; + + InstallResult installResult = VAppManagerService.get().installPackage(stageDir.getPath(), 0); + destroyInternal(); + dispatchSessionFinished(installResult.isSuccess ? INSTALL_SUCCEEDED : INSTALL_FAILED_INTERNAL_ERROR, installResult.toString(), null); } private void validateInstallLocked() throws PackageManagerException { @@ -193,13 +199,13 @@ private void validateInstallLocked() throws PackageManagerException { final String targetName = "base.apk"; final File targetFile = new File(mResolvedStageDir, targetName); if (!addedFile.equals(targetFile)) { - addedFile.renameTo(targetFile); + mResolvedStagedFiles.add(addedFile); + } else { + mResolvedBaseFile = targetFile; } - mResolvedBaseFile = targetFile; - mResolvedStagedFiles.add(targetFile); } } - if (mResolvedBaseFile == null) { + if (mResolvedBaseFile == null && mResolvedStagedFiles.isEmpty()) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Full install must include a base package"); } @@ -380,6 +386,11 @@ public void close() throws RemoteException { } } + // https://cs.android.com/android/platform/superproject/+/android-11.0.0_r1:frameworks/base/core/java/android/content/pm/IPackageInstallerSession.aidl;l=39 + public void commit(IntentSender statusReceiver, boolean forTransferred) throws RemoteException { + commit(statusReceiver); + } + @Override public void commit(IntentSender statusReceiver) throws RemoteException { final boolean wasSealed; diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/installer/VPackageInstallerService.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/installer/VPackageInstallerService.java index 20be4d587..98304cb39 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/installer/VPackageInstallerService.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/installer/VPackageInstallerService.java @@ -29,6 +29,7 @@ import com.lody.virtual.server.IPackageInstaller; import com.lody.virtual.server.pm.VAppManagerService; +import java.io.File; import java.io.IOException; import java.security.SecureRandom; import java.util.ArrayList; @@ -115,7 +116,11 @@ private int createSessionInternal(SessionParams params, String installerPackageN "Too many active sessions for UID " + callingUid); } sessionId = allocateSessionIdLocked(); - session = new PackageInstallerSession(mInternalCallback, mContext, mInstallHandler.getLooper(), installerPackageName, sessionId, userId, callingUid, params, VEnvironment.getPackageInstallerStageDir()); + File sessionDir = new File(VEnvironment.getPackageInstallerStageDir(), "vmd-" + sessionId); + session = new PackageInstallerSession(mInternalCallback, mContext, mInstallHandler.getLooper(), installerPackageName, sessionId, userId, callingUid, params, sessionDir); + } + synchronized (mSessions) { + mSessions.put(sessionId, session); } mCallbacks.notifySessionCreated(session.sessionId, session.userId); return sessionId; diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/parser/PackageParserEx.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/parser/PackageParserEx.java index fcc7a2243..e1d69702c 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/parser/PackageParserEx.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/parser/PackageParserEx.java @@ -14,6 +14,7 @@ import android.content.pm.ServiceInfo; import android.content.pm.Signature; import android.os.Build; +import android.os.Bundle; import android.os.Parcel; import android.text.TextUtils; @@ -21,6 +22,7 @@ import com.lody.virtual.client.env.Constants; import com.lody.virtual.client.fixer.ComponentFixer; import com.lody.virtual.helper.collection.ArrayMap; +import com.lody.virtual.helper.compat.BuildCompat; import com.lody.virtual.helper.compat.PackageParserCompat; import com.lody.virtual.helper.utils.FileUtils; import com.lody.virtual.helper.utils.VLog; @@ -121,6 +123,9 @@ public static void readSignature(VPackage pkg) { p.unmarshall(bytes, 0, bytes.length); p.setDataPosition(0); pkg.mSignatures = p.createTypedArray(Signature.CREATOR); + if (BuildCompat.isPie()) { + pkg.signingInfo = p.readParcelable(Bundle.class.getClassLoader()); + } } catch (IOException e) { e.printStackTrace(); } finally { @@ -151,6 +156,9 @@ public static void savePackageCache(VPackage pkg) { p = Parcel.obtain(); try { p.writeTypedArray(signatures, 0); + if (BuildCompat.isPie()) { + p.writeParcelable(pkg.signingInfo, 0); + } FileUtils.writeParcelToFile(p, signatureFile); } catch (IOException e) { e.printStackTrace(); @@ -212,6 +220,7 @@ private static VPackage buildPackageCache(PackageParser.Package p) { System.arraycopy(signatures, 0, cache.mSignatures, 0, numberOfSigs); } } + cache.signingInfo = mirror.android.content.pm.PackageParser.SigningInfo.ctor.newInstance(signingDetails); } cache.mAppMetaData = p.mAppMetaData; cache.packageName = p.packageName; @@ -224,6 +233,17 @@ private static VPackage buildPackageCache(PackageParser.Package p) { cache.mAppMetaData = p.mAppMetaData; cache.configPreferences = p.configPreferences; cache.reqFeatures = p.reqFeatures; + + // for split apks + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + cache.splitNames = p.splitNames; + cache.codePath = p.codePath; + cache.baseCodePath = p.baseCodePath; + cache.splitCodePaths = p.splitCodePaths; + } + + cache.usesOptionalLibraries = p.usesOptionalLibraries; + addOwner(cache); return cache; } @@ -241,8 +261,9 @@ public static void initApplicationInfoBase(PackageSetting ps, VPackage p) { ai.publicSourceDir = ps.apkPath; ai.sourceDir = ps.apkPath; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - ai.splitSourceDirs = new String[]{ps.apkPath}; - ai.splitPublicSourceDirs = ai.splitSourceDirs; + ai.splitSourceDirs = ps.splitCodePaths; + ai.splitPublicSourceDirs = ps.splitCodePaths; + ApplicationInfoL.scanSourceDir.set(ai, ai.dataDir); ApplicationInfoL.scanPublicSourceDir.set(ai, ai.dataDir); String hostPrimaryCpuAbi = ApplicationInfoL.primaryCpuAbi.get(VirtualCore.get().getContext().getApplicationInfo()); @@ -413,16 +434,31 @@ public static PackageInfo generatePackageInfo(VPackage p, int flags, long firstI } } } - if ((flags & PackageManager.GET_SIGNATURES) != 0) { + boolean hasGetSignatureFlag = (flags & PackageManager.GET_SIGNATURES) != 0; + boolean hasGetSigningInfoFlag = BuildCompat.isPie() && (flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0; + + if (hasGetSignatureFlag || hasGetSigningInfoFlag) { int N = (p.mSignatures != null) ? p.mSignatures.length : 0; if (N > 0) { pi.signatures = new Signature[N]; System.arraycopy(p.mSignatures, 0, pi.signatures, 0, N); } } + if (hasGetSigningInfoFlag) { + pi.signingInfo = p.signingInfo; + } return pi; } + private static boolean copyNeeded(int flags, VPackage p, + PackageUserState state, Bundle metaData, int userId) { + if (!state.installed || state.hidden) { + return true; + } + return (flags & PackageManager.GET_META_DATA) != 0 + && (metaData != null || p.mAppMetaData != null); + } + public static ApplicationInfo generateApplicationInfo(VPackage p, int flags, PackageUserState state, int userId) { if (p == null) return null; @@ -430,6 +466,11 @@ public static ApplicationInfo generateApplicationInfo(VPackage p, int flags, return null; } + if (!copyNeeded(flags, p, state, null, userId)) { + initApplicationAsUser(p.applicationInfo, userId); + return p.applicationInfo; + } + // Make shallow copy so we can store the metadata/libraries safely ApplicationInfo ai = new ApplicationInfo(p.applicationInfo); if ((flags & PackageManager.GET_META_DATA) != 0) { diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/parser/VPackage.java b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/parser/VPackage.java index d62a3e4d4..4000442ea 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/parser/VPackage.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/parser/VPackage.java @@ -14,11 +14,14 @@ import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; import android.content.pm.Signature; +import android.content.pm.SigningInfo; import android.os.Build; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import com.lody.virtual.helper.compat.BuildCompat; + import java.util.ArrayList; import java.util.HashSet; import java.util.Set; @@ -65,6 +68,23 @@ public VPackage[] newArray(int size) { public ArrayList reqFeatures = null; public Object mExtras; + public String[] splitNames; + public ArrayList usesOptionalLibraries; + + /** + * Path where this package was found on disk. For monolithic packages + * this is path to single base APK file; for cluster packages this is + * path to the cluster directory. + */ + public String codePath; + + /** Path of base APK */ + public String baseCodePath; + /** Paths of any split APKs, ordered by parsed splitName */ + public String[] splitCodePaths; + + public SigningInfo signingInfo; + public VPackage() { } @@ -117,6 +137,17 @@ protected VPackage(Parcel in) { this.mSharedUserLabel = in.readInt(); this.configPreferences = in.createTypedArrayList(ConfigurationInfo.CREATOR); this.reqFeatures = in.createTypedArrayList(FeatureInfo.CREATOR); + + this.splitNames = in.createStringArray(); + this.codePath = in.readString(); + this.baseCodePath = in.readString(); + this.splitCodePaths = in.createStringArray(); + + this.usesOptionalLibraries = in.createStringArrayList(); + + if (BuildCompat.isPie()) { + this.signingInfo = in.readParcelable(Bundle.class.getClassLoader()); + } } @Override @@ -223,6 +254,17 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(this.mSharedUserLabel); dest.writeTypedList(this.configPreferences); dest.writeTypedList(this.reqFeatures); + + dest.writeStringArray(this.splitNames); + dest.writeString(this.codePath); + dest.writeString(this.baseCodePath); + dest.writeStringArray(this.splitCodePaths); + + dest.writeStringList(this.usesOptionalLibraries); + + if (BuildCompat.isPie()) { + dest.writeParcelable(this.signingInfo, flags); + } } public static class ActivityIntentInfo extends IntentInfo { diff --git a/VirtualApp/lib/src/main/java/mirror/android/app/ContextImpl.java b/VirtualApp/lib/src/main/java/mirror/android/app/ContextImpl.java index fd495fb39..8718c3247 100644 --- a/VirtualApp/lib/src/main/java/mirror/android/app/ContextImpl.java +++ b/VirtualApp/lib/src/main/java/mirror/android/app/ContextImpl.java @@ -17,4 +17,6 @@ public class ContextImpl { public static RefObject mPackageManager; public static RefMethod getReceiverRestrictedContext; + + public static RefMethod getAttributionSource; } diff --git a/VirtualApp/lib/src/main/java/mirror/android/content/pm/PackageParser.java b/VirtualApp/lib/src/main/java/mirror/android/content/pm/PackageParser.java index 38a4ac02e..de8086cae 100644 --- a/VirtualApp/lib/src/main/java/mirror/android/content/pm/PackageParser.java +++ b/VirtualApp/lib/src/main/java/mirror/android/content/pm/PackageParser.java @@ -16,12 +16,12 @@ import java.io.File; import java.util.List; +import mirror.MethodParams; +import mirror.MethodReflectParams; import mirror.RefClass; import mirror.RefConstructor; -import mirror.RefObject; import mirror.RefMethod; -import mirror.MethodParams; -import mirror.MethodReflectParams; +import mirror.RefObject; import mirror.RefStaticMethod; /** @@ -101,6 +101,13 @@ public static class Component { public static RefObject> intents; } + public static class SigningInfo { + public static Class TYPE = RefClass.load(SigningInfo.class, "android.content.pm.SigningInfo"); + + @MethodReflectParams("android.content.pm.PackageParser$SigningDetails") + public static RefConstructor ctor; + } + public static class SigningDetails { public static Class TYPE = RefClass.load(SigningDetails.class, "android.content.pm.PackageParser$SigningDetails"); public static RefObject signatures; diff --git a/VirtualApp/lib/src/main/java/mirror/android/os/IDeviceIdentifiersPolicyService.java b/VirtualApp/lib/src/main/java/mirror/android/os/IDeviceIdentifiersPolicyService.java new file mode 100644 index 000000000..b06293f29 --- /dev/null +++ b/VirtualApp/lib/src/main/java/mirror/android/os/IDeviceIdentifiersPolicyService.java @@ -0,0 +1,24 @@ +package mirror.android.os; + +import android.annotation.TargetApi; +import android.os.IBinder; +import android.os.IInterface; + +import mirror.MethodParams; +import mirror.RefClass; +import mirror.RefStaticMethod; + +/** + * @author weishu + * @date 2021/8/19. + */ +@TargetApi(29) +public class IDeviceIdentifiersPolicyService { + public static Class TYPE = RefClass.load(IDeviceIdentifiersPolicyService.class, "android.os.IDeviceIdentifiersPolicyService"); + + public static class Stub { + public static Class TYPE = RefClass.load(IDeviceIdentifiersPolicyService.Stub.class, "android.os.IDeviceIdentifiersPolicyService$Stub"); + @MethodParams({IBinder.class}) + public static RefStaticMethod asInterface; + } +} diff --git a/VirtualApp/lib/src/main/java/mirror/com/android/internal/app/IBatteryStats.java b/VirtualApp/lib/src/main/java/mirror/com/android/internal/app/IBatteryStats.java new file mode 100644 index 000000000..df3e44c5b --- /dev/null +++ b/VirtualApp/lib/src/main/java/mirror/com/android/internal/app/IBatteryStats.java @@ -0,0 +1,23 @@ +package mirror.com.android.internal.app; + +import android.os.IBinder; +import android.os.IInterface; + +import mirror.MethodParams; +import mirror.RefClass; +import mirror.RefStaticMethod; + +/** + * @author weishu + * @date 2020/11/24. + */ + +public class IBatteryStats { + public static Class TYPE = RefClass.load(IBatteryStats.class, "com.android.internal.app.IBatteryStats"); + + public static class Stub { + public static Class TYPE = RefClass.load(IBatteryStats.Stub.class, "com.android.internal.app.IBatteryStats$Stub"); + @MethodParams({IBinder.class}) + public static RefStaticMethod asInterface; + } +} diff --git a/VirtualApp/lib/src/main/java/mirror/com/android/internal/os/health/SystemHealthManager.java b/VirtualApp/lib/src/main/java/mirror/com/android/internal/os/health/SystemHealthManager.java new file mode 100644 index 000000000..2ffe17e73 --- /dev/null +++ b/VirtualApp/lib/src/main/java/mirror/com/android/internal/os/health/SystemHealthManager.java @@ -0,0 +1,17 @@ +package mirror.com.android.internal.os.health; + +import android.os.IInterface; + +import mirror.RefClass; +import mirror.RefObject; + +/** + * @author weishu + * @date 2020/11/24. + */ + +public class SystemHealthManager { + public static Class TYPE = RefClass.load(SystemHealthManager.class, "android.os.health.SystemHealthManager"); + + public static RefObject mBatteryStats; +} diff --git a/VirtualApp/lib/src/main/java/mirror/mirror/android/security/net/config/ApplicationConfig.java b/VirtualApp/lib/src/main/java/mirror/mirror/android/security/net/config/ApplicationConfig.java new file mode 100644 index 000000000..ada7d2081 --- /dev/null +++ b/VirtualApp/lib/src/main/java/mirror/mirror/android/security/net/config/ApplicationConfig.java @@ -0,0 +1,10 @@ +package mirror.android.security.net.config; + +import mirror.RefClass; +import mirror.RefStaticMethod; + +public class ApplicationConfig { + public static Class TYPE = RefClass.load(ApplicationConfig.class, "android.security.net.config.ApplicationConfig"); + + public static RefStaticMethod setDefaultInstance; +} diff --git a/VirtualApp/lib/src/main/jni/Foundation/VMPatch.cpp b/VirtualApp/lib/src/main/jni/Foundation/VMPatch.cpp index 7c2c7a89a..6cdef6cf6 100644 --- a/VirtualApp/lib/src/main/jni/Foundation/VMPatch.cpp +++ b/VirtualApp/lib/src/main/jni/Foundation/VMPatch.cpp @@ -245,12 +245,24 @@ void mark() { // Do nothing }; +static size_t getArtMethodAddress(jobject javaMethod, jmethodID methodId) { + if (patchEnv.api_level < 30) { + return (size_t) methodId; + } else { + JNIEnv *env = Environment::current(); + jclass executableClass = env->FindClass("java/lang/reflect/Executable"); + jfieldID artMethod = env->GetFieldID(executableClass, "artMethod", "J"); + jlong addr = env->GetLongField(javaMethod, artMethod); + return addr; + } +} void measureNativeOffset(bool isArt) { jmethodID markMethod = nativeEngineClass->getStaticMethod("nativeMark").getId(); - size_t startAddress = (size_t) markMethod; + jobject method = Environment::current()->ToReflectedMethod(nativeEngineClass.get(), markMethod, JNI_TRUE); + size_t startAddress = (size_t) getArtMethodAddress(method, markMethod); size_t targetAddress = (size_t) mark; if (isArt && patchEnv.art_work_around_app_jni_bugs) { targetAddress = (size_t) patchEnv.art_work_around_app_jni_bugs; @@ -294,8 +306,8 @@ inline void replaceGetCallingUid(jboolean isArt) { inline void replaceOpenDexFileMethod(jobject javaMethod, jboolean isArt, int apiLevel) { - - size_t mtd_openDexNative = (size_t) Environment::current()->FromReflectedMethod(javaMethod); + jmethodID openDexNative = Environment::current()->FromReflectedMethod(javaMethod); + size_t mtd_openDexNative = getArtMethodAddress(javaMethod, openDexNative); int nativeFuncOffset = patchEnv.native_offset; void **jniFuncPtr = (void **) (mtd_openDexNative + nativeFuncOffset); @@ -321,7 +333,8 @@ replaceCameraNativeSetupMethod(jobject javaMethod, jboolean isArt, int apiLevel) if (!javaMethod) { return; } - size_t mtd_cameraNativeSetup = (size_t) Environment::current()->FromReflectedMethod(javaMethod); + jmethodID cameraNativeSetup = Environment::current()->FromReflectedMethod(javaMethod); + size_t mtd_cameraNativeSetup = getArtMethodAddress(javaMethod, cameraNativeSetup); int nativeFuncOffset = patchEnv.native_offset; void **jniFuncPtr = (void **) (mtd_cameraNativeSetup + nativeFuncOffset); @@ -358,7 +371,8 @@ replaceAudioRecordNativeCheckPermission(jobject javaMethod, jboolean isArt, int return; } jmethodID methodStruct = Environment::current()->FromReflectedMethod(javaMethod); - void **funPtr = (void **) (reinterpret_cast(methodStruct) + patchEnv.native_offset); + size_t mtd_methodStruct = getArtMethodAddress(javaMethod, methodStruct); + void **funPtr = (void **) (mtd_methodStruct + patchEnv.native_offset); patchEnv.orig_audioRecordNativeCheckPermission = (Function_audioRecordNativeCheckPermission) (*funPtr); *funPtr = (void *) new_native_audioRecordNativeCheckPermission; } @@ -427,10 +441,8 @@ void hookAndroidVM(JArrayClass javaMethods, } } measureNativeOffset(isArt); - // Crash on Q if hook directly by modify entrypoint of function. - // Just skip this step on Q and get never crash - if(apiLevel<=28) - replaceGetCallingUid(isArt); + replaceGetCallingUid(isArt); + replaceOpenDexFileMethod(javaMethods.getElement(OPEN_DEX).get(), isArt, apiLevel); replaceCameraNativeSetupMethod(javaMethods.getElement(CAMERA_SETUP).get(), diff --git a/VirtualApp/lib/src/main/jni/fb/jni/Environment.cpp b/VirtualApp/lib/src/main/jni/fb/jni/Environment.cpp index 48060107e..ee51f4fc1 100644 --- a/VirtualApp/lib/src/main/jni/fb/jni/Environment.cpp +++ b/VirtualApp/lib/src/main/jni/fb/jni/Environment.cpp @@ -90,10 +90,6 @@ void Environment::detachCurrentThread() { /* static */ JNIEnv* Environment::ensureCurrentThreadIsAttached() { - auto scope = currentScope(); - if (scope && scope->env_) { - return scope->env_; - } JNIEnv* env; // We should be able to just get the JNIEnv* by just calling @@ -104,7 +100,6 @@ JNIEnv* Environment::ensureCurrentThreadIsAttached() { FBASSERT(result == JNI_OK || result == JNI_EDETACHED); if (result == JNI_EDETACHED) { // The thread should not be detached while a ThreadScope is in the stack. - FBASSERT(!scope); env = attachCurrentThread(); } FBASSERT(env);