diff --git a/VirtualApp/lib/src/main/java/android/app/ClientTransactionHandler.java b/VirtualApp/lib/src/main/java/android/app/ClientTransactionHandler.java index 72a907825..1e346ab13 100644 --- a/VirtualApp/lib/src/main/java/android/app/ClientTransactionHandler.java +++ b/VirtualApp/lib/src/main/java/android/app/ClientTransactionHandler.java @@ -25,6 +25,7 @@ import android.os.IBinder; import android.util.MergedConfiguration; import android.view.DisplayAdjustments; +import android.window.SplashScreenViewParcelable; import java.util.List; import java.util.Map; @@ -202,4 +203,42 @@ public abstract void handleRelaunchActivity(ActivityThread.ActivityClientRecord /** Deliver new intent. */ public abstract void handleNewIntent(IBinder token, List intents); -} \ No newline at end of file + + /** + * Get {@link ActivityClientRecord} that is preparing to be launched. + * @param token Activity token. + * @return An initialized instance of {@link ActivityClientRecord} to use during launch. + */ + public abstract ActivityThread.ActivityClientRecord getLaunchingActivity(IBinder token); + + /** + * Remove {@link ActivityClientRecord} from the launching activity list. + * @param token Activity token. + * */ + public abstract void removeLaunchingActivity(IBinder token); + + /** Whether the activity want to handle splash screen exit animation */ + public abstract boolean isHandleSplashScreenExit(IBinder token); + + /** Hand over the splash screen window view to the activity */ + public abstract void handOverSplashScreenView(ActivityThread.ActivityClientRecord r); + + /** Attach a splash screen window view to the top of the activity */ + public abstract void handleAttachSplashScreenView(ActivityThread.ActivityClientRecord r, SplashScreenViewParcelable parcelable); + + public abstract void handlePictureInPictureStateChanged(ActivityThread.ActivityClientRecord r, PictureInPictureUiState pipState); + + /** Pause the activity. */ + public abstract void handlePauseActivity(ActivityThread.ActivityClientRecord r, boolean finished, + boolean userLeaving, int configChanges, PendingTransactionActions pendingActions, + String reason); + + /** Deliver result from another activity. */ + public abstract void handleSendResult(ActivityThread.ActivityClientRecord r, List results, String reason); + + public abstract void handleStopActivity(ActivityThread.ActivityClientRecord r, int configChanges, PendingTransactionActions pendingActions, boolean finalStateRequest, String reason); + public abstract void handleDestroyActivity(ActivityThread.ActivityClientRecord r, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason); + + /** Restart the activity after it was stopped. */ + public abstract void performRestartActivity(ActivityThread.ActivityClientRecord r, boolean start); +} diff --git a/VirtualApp/lib/src/main/java/android/app/PictureInPictureUiState.java b/VirtualApp/lib/src/main/java/android/app/PictureInPictureUiState.java new file mode 100644 index 000000000..bd3dbbb5a --- /dev/null +++ b/VirtualApp/lib/src/main/java/android/app/PictureInPictureUiState.java @@ -0,0 +1,35 @@ +package android.app; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * @author LittleAngry + * @date 2021/11/28. + */ +public class PictureInPictureUiState implements Parcelable { + + protected PictureInPictureUiState(Parcel in) { + } + + public static final Creator CREATOR = new Creator() { + @Override + public PictureInPictureUiState createFromParcel(Parcel in) { + return new PictureInPictureUiState(in); + } + + @Override + public PictureInPictureUiState[] newArray(int size) { + return new PictureInPictureUiState[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int i) { + } +} diff --git a/VirtualApp/lib/src/main/java/android/app/ResultInfo.java b/VirtualApp/lib/src/main/java/android/app/ResultInfo.java new file mode 100644 index 000000000..04184d5f9 --- /dev/null +++ b/VirtualApp/lib/src/main/java/android/app/ResultInfo.java @@ -0,0 +1,35 @@ +package android.app; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * @author LittleAngry + * @date 2021/11/28. + */ + +public class ResultInfo implements Parcelable { + protected ResultInfo(Parcel in) { + } + + public static final Creator CREATOR = new Creator() { + @Override + public ResultInfo createFromParcel(Parcel in) { + return new ResultInfo(in); + } + + @Override + public ResultInfo[] newArray(int size) { + return new ResultInfo[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int i) { + } +} diff --git a/VirtualApp/lib/src/main/java/android/app/TransactionHandlerProxy.java b/VirtualApp/lib/src/main/java/android/app/TransactionHandlerProxy.java index 5d1c10701..c9a326503 100644 --- a/VirtualApp/lib/src/main/java/android/app/TransactionHandlerProxy.java +++ b/VirtualApp/lib/src/main/java/android/app/TransactionHandlerProxy.java @@ -13,6 +13,7 @@ import android.util.Log; import android.util.MergedConfiguration; import android.view.DisplayAdjustments; +import android.window.SplashScreenViewParcelable; import com.lody.virtual.client.VClientImpl; import com.lody.virtual.client.core.VirtualCore; @@ -271,4 +272,59 @@ public void countLaunchingActivities(int num) { public void handleNewIntent(IBinder token, List intents) { originalHandler.handleNewIntent(token, intents); } + + @Override + public ActivityClientRecord getLaunchingActivity(IBinder token) { + return originalHandler.getLaunchingActivity(token); + } + + @Override + public void removeLaunchingActivity(IBinder token) { + originalHandler.removeLaunchingActivity(token); + } + + @Override + public boolean isHandleSplashScreenExit(IBinder token) { + return originalHandler.isHandleSplashScreenExit(token); + } + + @Override + public void handOverSplashScreenView(ActivityClientRecord r) { + originalHandler.handOverSplashScreenView(r); + } + + @Override + public void handleAttachSplashScreenView(ActivityClientRecord r, SplashScreenViewParcelable parcelable) { + originalHandler.handleAttachSplashScreenView(r, parcelable); + } + + @Override + public void handlePictureInPictureStateChanged(ActivityClientRecord r, PictureInPictureUiState pipState) { + originalHandler.handlePictureInPictureStateChanged(r, pipState); + } + + @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 handleSendResult(ActivityClientRecord r, List results, String reason) { + originalHandler.handleSendResult(r, results, 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 handleDestroyActivity(ActivityClientRecord r, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) { + originalHandler.handleDestroyActivity(r, finishing, configChanges, getNonConfigInstance, reason); + } + + @Override + public void performRestartActivity(ActivityClientRecord r, boolean start) { + originalHandler.performRestartActivity(r, start); + } } diff --git a/VirtualApp/lib/src/main/java/android/window/SplashScreenViewParcelable.java b/VirtualApp/lib/src/main/java/android/window/SplashScreenViewParcelable.java new file mode 100644 index 000000000..5754af152 --- /dev/null +++ b/VirtualApp/lib/src/main/java/android/window/SplashScreenViewParcelable.java @@ -0,0 +1,35 @@ +package android.window; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * @author LittleAngry + * @date 2021/11/28. + */ +public class SplashScreenViewParcelable implements Parcelable { + + protected SplashScreenViewParcelable(Parcel in) { + } + + public static final Creator CREATOR = new Creator() { + @Override + public SplashScreenViewParcelable createFromParcel(Parcel in) { + return new SplashScreenViewParcelable(in); + } + + @Override + public SplashScreenViewParcelable[] newArray(int size) { + return new SplashScreenViewParcelable[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int i) { + } +} 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 339465545..8cf8cd255 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 @@ -103,6 +103,9 @@ public final class VirtualCore { private ComponentDelegate componentDelegate; private TaskDescriptionDelegate taskDescriptionDelegate; + public String obbDir; + + private VirtualCore() { } @@ -185,6 +188,7 @@ public void startup(Context context) throws Throwable { throw new IllegalStateException("VirtualCore.startup() must called in main thread."); } Reflection.unseal(context); + obbDir = context.getObbDir().toString(); VASettings.STUB_CP_AUTHORITY = context.getPackageName() + "." + VASettings.STUB_DEF_AUTHORITY; ServiceManagerNative.SERVICE_CP_AUTH = context.getPackageName() + "." + ServiceManagerNative.SERVICE_DEF_AUTH; 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 f1db2ecd7..e9a53f102 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,7 @@ import android.os.IInterface; import android.os.ParcelFileDescriptor; +import com.lody.virtual.client.core.VirtualCore; import com.lody.virtual.client.hook.base.MethodBox; import com.lody.virtual.helper.compat.BuildCompat; import com.lody.virtual.helper.utils.VLog; @@ -21,6 +22,8 @@ import java.util.HashMap; import java.util.Map; +import mirror.android.content.AttributionSource; +import mirror.android.content.AttributionSourceState; import mirror.android.content.IContentProvider; /** @@ -144,6 +147,11 @@ public AssetFileDescriptor openAssetFile(MethodBox methodBox, Uri url, String mo return (AssetFileDescriptor) methodBox.call(); } + public void fixAttributionSource(Object attributionSource) { + AttributionSourceState.packageName.set(AttributionSource.mAttributionSourceState.get(attributionSource), VirtualCore.get().getContext().getPackageName()); + AttributionSourceState.uid.set(AttributionSource.mAttributionSourceState.get(attributionSource), VirtualCore.get().myUid()); + } + @Override public Object invoke(Object proxy, Method method, Object... args) throws Throwable { try { @@ -158,7 +166,12 @@ public Object invoke(Object proxy, Method method, Object... args) throws Throwab try { String name = method.getName(); if ("call".equals(name)) { - if (BuildCompat.isR()) { + if(Build.VERSION.SDK_INT >= 31) + fixAttributionSource(args[0]); + + if (Build.VERSION.SDK_INT >= 31) { + start = 2; + } else if (BuildCompat.isR()) { start = 3; } else if (BuildCompat.isQ()) { start = 2; @@ -168,35 +181,58 @@ public Object invoke(Object proxy, Method method, Object... args) throws Throwab Bundle extras = (Bundle) args[start + 2]; return call(methodBox, methodName, arg, extras); } else if ("insert".equals(name)) { + if(Build.VERSION.SDK_INT >= 31) + fixAttributionSource(args[0]); + Uri url = (Uri) args[start]; ContentValues initialValues = (ContentValues) args[start + 1]; return insert(methodBox, url, initialValues); } else if ("getType".equals(name)) { return getType(methodBox, (Uri) args[0]); } else if ("delete".equals(name)) { + if(Build.VERSION.SDK_INT >= 31) + fixAttributionSource(args[0]); + Uri url = (Uri) args[start]; String selection = (String) args[start + 1]; String[] selectionArgs = (String[]) args[start + 2]; return delete(methodBox, url, selection, selectionArgs); } else if ("bulkInsert".equals(name)) { + if(Build.VERSION.SDK_INT >= 31) + fixAttributionSource(args[0]); + Uri url = (Uri) args[start]; ContentValues[] initialValues = (ContentValues[]) args[start + 1]; return bulkInsert(methodBox, url, initialValues); } else if ("update".equals(name)) { + if(Build.VERSION.SDK_INT >= 31) + fixAttributionSource(args[0]); + Uri url = (Uri) args[start]; ContentValues values = (ContentValues) args[start + 1]; String selection = (String) args[start + 2]; String[] selectionArgs = (String[]) args[start + 3]; return update(methodBox, url, values, selection, selectionArgs); } else if ("openFile".equals(name)) { + if(Build.VERSION.SDK_INT >= 31) + fixAttributionSource(args[0]); + Uri url = (Uri) args[start]; String mode = (String) args[start + 1]; return openFile(methodBox, url, mode); } else if ("openAssetFile".equals(name)) { + if(Build.VERSION.SDK_INT >= 31) + fixAttributionSource(args[0]); + Uri url = (Uri) args[start]; String mode = (String) args[start + 1]; return openAssetFile(methodBox, url, mode); } else if ("query".equals(name)) { + if(Build.VERSION.SDK_INT >= 31) { + fixAttributionSource(args[0]); + start--; + } + Uri url = (Uri) args[start]; String[] projection = (String[]) args[start + 1]; String selection = null; 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 8199cbe3a..0a81230b0 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 @@ -3,8 +3,10 @@ import android.location.LocationManager; import android.location.LocationRequest; import android.os.Build; +import android.os.Debug; import com.lody.virtual.client.core.VirtualCore; +import com.lody.virtual.client.env.VirtualRuntime; import com.lody.virtual.client.hook.base.MethodProxy; import com.lody.virtual.client.hook.base.ReplaceLastPkgMethodProxy; import com.lody.virtual.client.ipc.VirtualLocationManager; @@ -16,6 +18,7 @@ import java.util.Arrays; import java.util.List; +import mirror.android.location.GeocoderParams; import mirror.android.location.LocationRequestL; /** @@ -318,4 +321,66 @@ public String getMethodName() { } } + static class RegisterLocationListener extends MethodProxy { + + @Override + public Object call(Object who, Method method, Object... args) throws Throwable { + args[3] = VirtualCore.get().getContext().getPackageName(); + return super.call(who, method, args); + } + + @Override + public String getMethodName() { + return "registerLocationListener"; + } + } + static class GetCurrentLocation extends MethodProxy { + + @Override + public Object call(Object who, Method method, Object... args) throws Throwable { + args[3] = VirtualCore.get().getContext().getPackageName(); + return super.call(who, method, args); + } + + @Override + public String getMethodName() { + return "getCurrentLocation"; + } + } + static class GetFromLocation extends MethodProxy { + + @Override + public Object call(Object who, Method method, Object... args) throws Throwable { + Object geocodeParams = args[3]; + + GeocoderParams.mPackageName.set(geocodeParams, VirtualCore.get().getContext().getPackageName()); + GeocoderParams.mUid.set(geocodeParams, VirtualCore.get().myUid()); + + return super.call(who, method, args); + } + + @Override + public String getMethodName() { + return "getFromLocation"; + } + } + + static class GetFromLocationName extends MethodProxy { + + @Override + public Object call(Object who, Method method, Object... args) throws Throwable { + Object geocodeParams = args[6]; + + GeocoderParams.mPackageName.set(geocodeParams, VirtualCore.get().getContext().getPackageName()); + GeocoderParams.mUid.set(geocodeParams, VirtualCore.get().myUid()); + + return super.call(who, method, args); + } + + @Override + public String getMethodName() { + return "getFromLocationName"; + } + } + } diff --git a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/mount/MethodProxies.java b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/mount/MethodProxies.java index 608fff712..8357442b4 100644 --- a/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/mount/MethodProxies.java +++ b/VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/mount/MethodProxies.java @@ -2,6 +2,7 @@ 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.utils.MethodParameterUtils; @@ -39,6 +40,20 @@ public Object afterCall(Object who, Method method, Object[] args, Object result) } } + static class FixupAppDir extends MethodProxy { + + @Override + public String getMethodName() { + return "fixupAppDir"; + } + + @Override + public Object call(Object who, Method method, Object... args) throws Throwable { + args[0] = VirtualCore.get().obbDir; + return super.call(who, method, args); + } + } + static class Mkdirs extends MethodProxy { @Override diff --git a/VirtualApp/lib/src/main/java/mirror/android/content/AttributionSource.java b/VirtualApp/lib/src/main/java/mirror/android/content/AttributionSource.java new file mode 100644 index 000000000..32cf2ac53 --- /dev/null +++ b/VirtualApp/lib/src/main/java/mirror/android/content/AttributionSource.java @@ -0,0 +1,10 @@ +package mirror.android.content; + +import mirror.RefClass; +import mirror.RefObject; + +public class AttributionSource { + public static Class TYPE = RefClass.load(AttributionSource.class, "android.content.AttributionSource"); + + public static RefObject mAttributionSourceState; +} diff --git a/VirtualApp/lib/src/main/java/mirror/android/content/AttributionSourceState.java b/VirtualApp/lib/src/main/java/mirror/android/content/AttributionSourceState.java new file mode 100644 index 000000000..311f5af7f --- /dev/null +++ b/VirtualApp/lib/src/main/java/mirror/android/content/AttributionSourceState.java @@ -0,0 +1,12 @@ +package mirror.android.content; + +import mirror.RefClass; +import mirror.RefObject; + +public class AttributionSourceState { + public static Class TYPE = RefClass.load(AttributionSourceState.class, "android.content.AttributionSourceState"); + + public static RefObject packageName; + public static RefObject uid; + +} diff --git a/VirtualApp/lib/src/main/java/mirror/android/location/GeocoderParams.java b/VirtualApp/lib/src/main/java/mirror/android/location/GeocoderParams.java new file mode 100644 index 000000000..f557823bd --- /dev/null +++ b/VirtualApp/lib/src/main/java/mirror/android/location/GeocoderParams.java @@ -0,0 +1,16 @@ +package mirror.android.location; + +import mirror.RefClass; +import mirror.RefObject; + +/** + * @author LittleAngry + * @date 2021/11/28. + */ + +public class GeocoderParams { + public static Class TYPE = RefClass.load(GeocoderParams.class, "android.location.GeocoderParams"); + + public static RefObject mPackageName; + public static RefObject mUid; +}