Repository: android-hacker/VirtualXposed
Branch: vxp
Commit: 122beb371519
Files: 801
Total size: 2.7 MB
Directory structure:
gitextract_16jertsa/
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── bug_report_cn.md
│ │ ├── feature_request.md
│ │ └── feature_request_cn.md
│ ├── issue-close-app.yml
│ ├── stale.yml
│ └── workflows/
│ └── android.yml
├── .gitignore
├── .gitmodules
├── .travis.yml
├── CHINESE.md
├── LICENSE.txt
├── README.md
└── VirtualApp/
├── .gitignore
├── app/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ ├── aosp/
│ │ └── java/
│ │ └── io/
│ │ └── virtualapp/
│ │ └── delegate/
│ │ ├── MyCrashHandler.java
│ │ └── MyVirtualInitializer.java
│ ├── fdroid/
│ │ └── java/
│ │ └── io/
│ │ └── virtualapp/
│ │ └── delegate/
│ │ └── MyVirtualInitializer.java
│ └── main/
│ ├── AndroidManifest.xml
│ ├── assets/
│ │ └── XposedInstaller_3.1.5.apk_
│ ├── java/
│ │ └── io/
│ │ └── virtualapp/
│ │ ├── VCommends.java
│ │ ├── XApp.java
│ │ ├── abs/
│ │ │ ├── BasePresenter.java
│ │ │ ├── BaseView.java
│ │ │ ├── Callback.java
│ │ │ └── ui/
│ │ │ ├── VActivity.java
│ │ │ ├── VFragment.java
│ │ │ └── VUiKit.java
│ │ ├── delegate/
│ │ │ ├── BaseCrashHandler.java
│ │ │ ├── BaseVirtualInitializer.java
│ │ │ ├── MyAppRequestListener.java
│ │ │ ├── MyComponentDelegate.java
│ │ │ ├── MyPhoneInfoDelegate.java
│ │ │ └── MyTaskDescDelegate.java
│ │ ├── dev/
│ │ │ └── CmdReceiver.java
│ │ ├── glide/
│ │ │ ├── GlideUtils.java
│ │ │ ├── MyGlideModule.java
│ │ │ ├── PackageIconResourceDataFetcher.java
│ │ │ ├── PackageIconResourceLoader.java
│ │ │ └── PackageIconResourceLoaderFactory.java
│ │ ├── gms/
│ │ │ └── FakeGms.java
│ │ ├── home/
│ │ │ ├── ListAppActivity.java
│ │ │ ├── ListAppContract.java
│ │ │ ├── ListAppFragment.java
│ │ │ ├── ListAppPresenterImpl.java
│ │ │ ├── LoadingActivity.java
│ │ │ ├── NewHomeActivity.java
│ │ │ ├── adapters/
│ │ │ │ ├── AppPagerAdapter.java
│ │ │ │ └── CloneAppListAdapter.java
│ │ │ ├── models/
│ │ │ │ ├── AppData.java
│ │ │ │ ├── AppInfo.java
│ │ │ │ ├── AppInfoLite.java
│ │ │ │ ├── MultiplePackageAppData.java
│ │ │ │ └── PackageAppData.java
│ │ │ └── repo/
│ │ │ ├── AppDataSource.java
│ │ │ ├── AppRepository.java
│ │ │ └── PackageAppDataStorage.java
│ │ ├── settings/
│ │ │ ├── AboutActivity.java
│ │ │ ├── AppManageActivity.java
│ │ │ ├── NougatPolicy.java
│ │ │ ├── OnlinePlugin.java
│ │ │ ├── RecommendPluginActivity.java
│ │ │ ├── SettingsActivity.java
│ │ │ └── TaskManageActivity.java
│ │ ├── splash/
│ │ │ └── SplashActivity.java
│ │ ├── sys/
│ │ │ ├── Installd.java
│ │ │ ├── InstallerActivity.java
│ │ │ └── ShareBridgeActivity.java
│ │ ├── update/
│ │ │ └── VAVersionService.java
│ │ ├── utils/
│ │ │ ├── DialogUtil.java
│ │ │ ├── HanziToPinyin.java
│ │ │ └── Misc.java
│ │ └── widgets/
│ │ ├── BaseView.java
│ │ ├── CardStackAdapter.java
│ │ ├── CardStackLayout.java
│ │ ├── DragSelectRecyclerView.java
│ │ ├── DragSelectRecyclerViewAdapter.java
│ │ ├── EatBeansView.java
│ │ ├── Indicator.java
│ │ ├── LabelView.java
│ │ ├── MarqueeTextView.java
│ │ └── fittext/
│ │ ├── BaseTextView.java
│ │ ├── FitTextHelper.java
│ │ └── FitTextView.java
│ └── res/
│ ├── drawable/
│ │ ├── blue_circle.xml
│ │ ├── fab_bg.xml
│ │ ├── home_bg.xml
│ │ ├── icon_bg.xml
│ │ ├── sel_clone_app_btn.xml
│ │ ├── sel_guide_btn.xml
│ │ ├── shape_clone_app_btn.xml
│ │ └── shape_clone_app_btn_pressed.xml
│ ├── drawable-nodpi/
│ │ ├── about_icon_copy_right.xml
│ │ └── ic_more.xml
│ ├── layout/
│ │ ├── activity_clone_app.xml
│ │ ├── activity_install.xml
│ │ ├── activity_list.xml
│ │ ├── activity_loading.xml
│ │ ├── activity_location_settings.xml
│ │ ├── activity_marker.xml
│ │ ├── activity_splash.xml
│ │ ├── activity_users.xml
│ │ ├── content_toolbar.xml
│ │ ├── fragment_list_app.xml
│ │ ├── item_app.xml
│ │ ├── item_app_manage.xml
│ │ ├── item_clone_app.xml
│ │ ├── item_location_app.xml
│ │ ├── item_plugin_recommend.xml
│ │ ├── item_share.xml
│ │ ├── item_task_manage.xml
│ │ └── item_user.xml
│ ├── menu/
│ │ ├── app_manage_menu.xml
│ │ ├── main_menu.xml
│ │ └── marktet_map.xml
│ ├── values/
│ │ ├── attrs.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── fitTextView.xml
│ │ ├── ids.xml
│ │ ├── integers.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ ├── values-en/
│ │ └── strings.xml
│ ├── values-es/
│ │ └── strings.xml
│ ├── values-fr/
│ │ └── strings.xml
│ ├── values-ja/
│ │ └── strings.xml
│ ├── values-pt-rBR/
│ │ └── strings.xml
│ ├── values-ru/
│ │ └── strings.xml
│ ├── values-uk/
│ │ └── strings.xml
│ ├── values-zh-rCN/
│ │ └── strings.xml
│ ├── values-zh-rTW/
│ │ └── strings.xml
│ └── xml/
│ └── settings_preferences.xml
├── build.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── lib/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── aidl/
│ │ ├── android/
│ │ │ ├── accounts/
│ │ │ │ ├── IAccountAuthenticator.aidl
│ │ │ │ ├── IAccountAuthenticatorResponse.aidl
│ │ │ │ └── IAccountManagerResponse.aidl
│ │ │ ├── app/
│ │ │ │ ├── IActivityManager/
│ │ │ │ │ └── ContentProviderHolder.aidl
│ │ │ │ ├── IServiceConnection.aidl
│ │ │ │ ├── IStopUserCallback.aidl
│ │ │ │ └── job/
│ │ │ │ ├── IJobCallback.aidl
│ │ │ │ └── IJobService.aidl
│ │ │ ├── content/
│ │ │ │ ├── IIntentReceiver.aidl
│ │ │ │ ├── ISyncAdapter.aidl
│ │ │ │ ├── ISyncContext.aidl
│ │ │ │ ├── ISyncStatusObserver.aidl
│ │ │ │ └── pm/
│ │ │ │ ├── IPackageDataObserver.aidl
│ │ │ │ ├── IPackageDeleteObserver2.aidl
│ │ │ │ ├── IPackageInstallObserver.aidl
│ │ │ │ ├── IPackageInstallObserver2.aidl
│ │ │ │ ├── IPackageInstallerCallback.aidl
│ │ │ │ └── IPackageInstallerSession.aidl
│ │ │ ├── location/
│ │ │ │ └── ILocationListener.aidl
│ │ │ └── net/
│ │ │ ├── IConnectivityManager.aidl
│ │ │ └── wifi/
│ │ │ └── IWifiScanner.aidl
│ │ └── com/
│ │ └── lody/
│ │ └── virtual/
│ │ ├── client/
│ │ │ └── IVClient.aidl
│ │ ├── os/
│ │ │ └── VUserInfo.aidl
│ │ ├── remote/
│ │ │ ├── AppTaskInfo.aidl
│ │ │ ├── BadgerInfo.aidl
│ │ │ ├── InstallResult.aidl
│ │ │ ├── InstalledAppInfo.aidl
│ │ │ ├── PendingIntentData.aidl
│ │ │ ├── PendingResultData.aidl
│ │ │ ├── Problem.aidl
│ │ │ ├── ReceiverInfo.aidl
│ │ │ ├── VDeviceInfo.aidl
│ │ │ ├── VParceledListSlice.aidl
│ │ │ └── vloc/
│ │ │ ├── VCell.aidl
│ │ │ ├── VLocation.aidl
│ │ │ └── VWifi.aidl
│ │ └── server/
│ │ ├── IAccountManager.aidl
│ │ ├── IActivityManager.aidl
│ │ ├── IAppManager.aidl
│ │ ├── IBinderDelegateService.aidl
│ │ ├── IDeviceInfoManager.aidl
│ │ ├── IJobScheduler.aidl
│ │ ├── INotificationManager.aidl
│ │ ├── IPackageInstaller.aidl
│ │ ├── IPackageInstallerSession.aidl
│ │ ├── IPackageManager.aidl
│ │ ├── IUserManager.aidl
│ │ ├── IVirtualLocationManager.aidl
│ │ ├── IVirtualStorageService.aidl
│ │ ├── interfaces/
│ │ │ ├── IAppRequestListener.aidl
│ │ │ ├── IIntentFilterObserver.aidl
│ │ │ ├── IPackageObserver.aidl
│ │ │ ├── IProcessObserver.aidl
│ │ │ ├── IServiceFetcher.aidl
│ │ │ └── IUiCallback.aidl
│ │ └── pm/
│ │ └── installer/
│ │ ├── SessionInfo.aidl
│ │ └── SessionParams.aidl
│ ├── java/
│ │ ├── android/
│ │ │ ├── app/
│ │ │ │ ├── ActivityOptions.java
│ │ │ │ ├── ActivityThread.java
│ │ │ │ ├── ClientTransactionHandler.java
│ │ │ │ ├── LoadedApk.java
│ │ │ │ ├── TransactionHandlerProxy.java
│ │ │ │ └── servertransaction/
│ │ │ │ ├── ClientTransaction.java
│ │ │ │ ├── PendingTransactionActions.java
│ │ │ │ └── TransactionExecutor.java
│ │ │ ├── content/
│ │ │ │ ├── SyncStatusInfo.java
│ │ │ │ ├── pm/
│ │ │ │ │ └── PackageParser.java
│ │ │ │ └── res/
│ │ │ │ └── CompatibilityInfo.java
│ │ │ ├── location/
│ │ │ │ └── LocationRequest.java
│ │ │ ├── util/
│ │ │ │ └── MergedConfiguration.java
│ │ │ └── view/
│ │ │ └── DisplayAdjustments.java
│ │ ├── com/
│ │ │ └── lody/
│ │ │ └── virtual/
│ │ │ ├── Build.java
│ │ │ ├── DelegateApplication64Bit.java
│ │ │ ├── GmsSupport.java
│ │ │ ├── client/
│ │ │ │ ├── NativeEngine.java
│ │ │ │ ├── VClientImpl.java
│ │ │ │ ├── badger/
│ │ │ │ │ ├── BadgerManager.java
│ │ │ │ │ ├── BroadcastBadger1.java
│ │ │ │ │ ├── BroadcastBadger2.java
│ │ │ │ │ └── IBadger.java
│ │ │ │ ├── core/
│ │ │ │ │ ├── CrashHandler.java
│ │ │ │ │ ├── InstallStrategy.java
│ │ │ │ │ ├── InvocationStubManager.java
│ │ │ │ │ └── VirtualCore.java
│ │ │ │ ├── env/
│ │ │ │ │ ├── Constants.java
│ │ │ │ │ ├── DeadServerException.java
│ │ │ │ │ ├── GPSStateline.java
│ │ │ │ │ ├── SpecialComponentList.java
│ │ │ │ │ ├── VirtualGPSSatalines.java
│ │ │ │ │ └── VirtualRuntime.java
│ │ │ │ ├── fixer/
│ │ │ │ │ ├── ActivityFixer.java
│ │ │ │ │ ├── ComponentFixer.java
│ │ │ │ │ └── ContextFixer.java
│ │ │ │ ├── hook/
│ │ │ │ │ ├── base/
│ │ │ │ │ │ ├── BinderInvocationProxy.java
│ │ │ │ │ │ ├── BinderInvocationStub.java
│ │ │ │ │ │ ├── Inject.java
│ │ │ │ │ │ ├── LogInvocation.java
│ │ │ │ │ │ ├── MethodBox.java
│ │ │ │ │ │ ├── MethodInvocationProxy.java
│ │ │ │ │ │ ├── MethodInvocationStub.java
│ │ │ │ │ │ ├── MethodProxy.java
│ │ │ │ │ │ ├── ReplaceCallingPkgMethodProxy.java
│ │ │ │ │ │ ├── ReplaceLastPkgMethodProxy.java
│ │ │ │ │ │ ├── ReplaceLastUidMethodProxy.java
│ │ │ │ │ │ ├── ReplaceSequencePkgMethodProxy.java
│ │ │ │ │ │ ├── ReplaceSpecPkgMethodProxy.java
│ │ │ │ │ │ ├── ReplaceUidMethodProxy.java
│ │ │ │ │ │ ├── ResultStaticMethodProxy.java
│ │ │ │ │ │ ├── SkipInject.java
│ │ │ │ │ │ └── StaticMethodProxy.java
│ │ │ │ │ ├── delegate/
│ │ │ │ │ │ ├── AppInstrumentation.java
│ │ │ │ │ │ ├── ComponentDelegate.java
│ │ │ │ │ │ ├── InstrumentationDelegate.java
│ │ │ │ │ │ ├── PhoneInfoDelegate.java
│ │ │ │ │ │ └── TaskDescriptionDelegate.java
│ │ │ │ │ ├── providers/
│ │ │ │ │ │ ├── DownloadProviderHook.java
│ │ │ │ │ │ ├── ExternalProviderHook.java
│ │ │ │ │ │ ├── InternalProviderHook.java
│ │ │ │ │ │ ├── MediaProviderHook.java
│ │ │ │ │ │ ├── ProviderHook.java
│ │ │ │ │ │ ├── QueryRedirectCursor.java
│ │ │ │ │ │ └── SettingsProviderHook.java
│ │ │ │ │ ├── proxies/
│ │ │ │ │ │ ├── account/
│ │ │ │ │ │ │ └── AccountManagerStub.java
│ │ │ │ │ │ ├── alarm/
│ │ │ │ │ │ │ └── AlarmManagerStub.java
│ │ │ │ │ │ ├── am/
│ │ │ │ │ │ │ ├── ActivityManagerStub.java
│ │ │ │ │ │ │ ├── ActivityTaskManagerStub.java
│ │ │ │ │ │ │ ├── HCallbackStub.java
│ │ │ │ │ │ │ ├── MethodProxies.java
│ │ │ │ │ │ │ └── TransactionHandlerStub.java
│ │ │ │ │ │ ├── appops/
│ │ │ │ │ │ │ └── AppOpsManagerStub.java
│ │ │ │ │ │ ├── appwidget/
│ │ │ │ │ │ │ └── AppWidgetManagerStub.java
│ │ │ │ │ │ ├── audio/
│ │ │ │ │ │ │ └── AudioManagerStub.java
│ │ │ │ │ │ ├── backup/
│ │ │ │ │ │ │ └── BackupManagerStub.java
│ │ │ │ │ │ ├── battery/
│ │ │ │ │ │ │ └── BatteryStatsStub.java
│ │ │ │ │ │ ├── bluetooth/
│ │ │ │ │ │ │ └── BluetoothStub.java
│ │ │ │ │ │ ├── clipboard/
│ │ │ │ │ │ │ └── ClipBoardStub.java
│ │ │ │ │ │ ├── connectivity/
│ │ │ │ │ │ │ └── ConnectivityStub.java
│ │ │ │ │ │ ├── content/
│ │ │ │ │ │ │ ├── ContentServiceStub.java
│ │ │ │ │ │ │ └── MethodProxies.java
│ │ │ │ │ │ ├── context_hub/
│ │ │ │ │ │ │ └── ContextHubServiceStub.java
│ │ │ │ │ │ ├── devicepolicy/
│ │ │ │ │ │ │ └── DevicePolicyManagerStub.java
│ │ │ │ │ │ ├── display/
│ │ │ │ │ │ │ └── DisplayStub.java
│ │ │ │ │ │ ├── dropbox/
│ │ │ │ │ │ │ └── DropBoxManagerStub.java
│ │ │ │ │ │ ├── fingerprint/
│ │ │ │ │ │ │ └── FingerprintManagerStub.java
│ │ │ │ │ │ ├── graphics/
│ │ │ │ │ │ │ └── GraphicsStatsStub.java
│ │ │ │ │ │ ├── imms/
│ │ │ │ │ │ │ └── MmsStub.java
│ │ │ │ │ │ ├── input/
│ │ │ │ │ │ │ ├── InputMethodManagerStub.java
│ │ │ │ │ │ │ └── MethodProxies.java
│ │ │ │ │ │ ├── isms/
│ │ │ │ │ │ │ └── ISmsStub.java
│ │ │ │ │ │ ├── isub/
│ │ │ │ │ │ │ └── ISubStub.java
│ │ │ │ │ │ ├── job/
│ │ │ │ │ │ │ └── JobServiceStub.java
│ │ │ │ │ │ ├── libcore/
│ │ │ │ │ │ │ ├── LibCoreStub.java
│ │ │ │ │ │ │ └── MethodProxies.java
│ │ │ │ │ │ ├── location/
│ │ │ │ │ │ │ ├── GPSListenerThread.java
│ │ │ │ │ │ │ ├── GPSStatusListenerThread.java
│ │ │ │ │ │ │ ├── LocationManagerStub.java
│ │ │ │ │ │ │ ├── MethodProxies.java
│ │ │ │ │ │ │ └── MockLocationHelper.java
│ │ │ │ │ │ ├── media/
│ │ │ │ │ │ │ ├── router/
│ │ │ │ │ │ │ │ └── MediaRouterServiceStub.java
│ │ │ │ │ │ │ └── session/
│ │ │ │ │ │ │ └── SessionManagerStub.java
│ │ │ │ │ │ ├── mount/
│ │ │ │ │ │ │ ├── MethodProxies.java
│ │ │ │ │ │ │ └── MountServiceStub.java
│ │ │ │ │ │ ├── network/
│ │ │ │ │ │ │ └── NetworkManagementStub.java
│ │ │ │ │ │ ├── notification/
│ │ │ │ │ │ │ ├── MethodProxies.java
│ │ │ │ │ │ │ └── NotificationManagerStub.java
│ │ │ │ │ │ ├── os/
│ │ │ │ │ │ │ └── DeviceIdentifiersPolicyServiceStub.java
│ │ │ │ │ │ ├── persistent_data_block/
│ │ │ │ │ │ │ └── PersistentDataBlockServiceStub.java
│ │ │ │ │ │ ├── phonesubinfo/
│ │ │ │ │ │ │ ├── MethodProxies.java
│ │ │ │ │ │ │ └── PhoneSubInfoStub.java
│ │ │ │ │ │ ├── pm/
│ │ │ │ │ │ │ ├── LauncherAppsStub.java
│ │ │ │ │ │ │ ├── MethodProxies.java
│ │ │ │ │ │ │ └── PackageManagerStub.java
│ │ │ │ │ │ ├── power/
│ │ │ │ │ │ │ └── PowerManagerStub.java
│ │ │ │ │ │ ├── restriction/
│ │ │ │ │ │ │ └── RestrictionStub.java
│ │ │ │ │ │ ├── search/
│ │ │ │ │ │ │ └── SearchManagerStub.java
│ │ │ │ │ │ ├── shortcut/
│ │ │ │ │ │ │ └── ShortcutServiceStub.java
│ │ │ │ │ │ ├── telephony/
│ │ │ │ │ │ │ ├── MethodProxies.java
│ │ │ │ │ │ │ ├── TelephonyRegistryStub.java
│ │ │ │ │ │ │ └── TelephonyStub.java
│ │ │ │ │ │ ├── usage/
│ │ │ │ │ │ │ └── UsageStatsManagerStub.java
│ │ │ │ │ │ ├── user/
│ │ │ │ │ │ │ └── UserManagerStub.java
│ │ │ │ │ │ ├── vibrator/
│ │ │ │ │ │ │ └── VibratorStub.java
│ │ │ │ │ │ ├── view/
│ │ │ │ │ │ │ └── AutoFillManagerStub.java
│ │ │ │ │ │ ├── wifi/
│ │ │ │ │ │ │ └── WifiManagerStub.java
│ │ │ │ │ │ ├── wifi_scanner/
│ │ │ │ │ │ │ ├── GhostWifiScannerImpl.java
│ │ │ │ │ │ │ └── WifiScannerStub.java
│ │ │ │ │ │ └── window/
│ │ │ │ │ │ ├── MethodProxies.java
│ │ │ │ │ │ ├── WindowManagerStub.java
│ │ │ │ │ │ └── session/
│ │ │ │ │ │ ├── BaseMethodProxy.java
│ │ │ │ │ │ └── WindowSessionPatch.java
│ │ │ │ │ ├── secondary/
│ │ │ │ │ │ ├── HackAppUtils.java
│ │ │ │ │ │ ├── ProxyServiceFactory.java
│ │ │ │ │ │ ├── ServiceConnectionDelegate.java
│ │ │ │ │ │ └── StubBinder.java
│ │ │ │ │ └── utils/
│ │ │ │ │ └── MethodParameterUtils.java
│ │ │ │ ├── interfaces/
│ │ │ │ │ └── IInjector.java
│ │ │ │ ├── ipc/
│ │ │ │ │ ├── ActivityClientRecord.java
│ │ │ │ │ ├── LocalProxyUtils.java
│ │ │ │ │ ├── ProviderCall.java
│ │ │ │ │ ├── ServiceManagerNative.java
│ │ │ │ │ ├── VAccountManager.java
│ │ │ │ │ ├── VActivityManager.java
│ │ │ │ │ ├── VDeviceManager.java
│ │ │ │ │ ├── VJobScheduler.java
│ │ │ │ │ ├── VNotificationManager.java
│ │ │ │ │ ├── VPackageManager.java
│ │ │ │ │ ├── VirtualLocationManager.java
│ │ │ │ │ └── VirtualStorageManager.java
│ │ │ │ ├── natives/
│ │ │ │ │ └── NativeMethods.java
│ │ │ │ └── stub/
│ │ │ │ ├── AmsTask.java
│ │ │ │ ├── ChooseAccountTypeActivity.java
│ │ │ │ ├── ChooseTypeAndAccountActivity.java
│ │ │ │ ├── ChooserActivity.java
│ │ │ │ ├── DaemonJobService.java
│ │ │ │ ├── DaemonService.java
│ │ │ │ ├── ResolverActivity.java
│ │ │ │ ├── ShortcutHandleActivity.java
│ │ │ │ ├── StubActivity.java
│ │ │ │ ├── StubCP.java
│ │ │ │ ├── StubDialog.java
│ │ │ │ ├── StubExcludeFromRecentActivity.java
│ │ │ │ ├── StubJob.java
│ │ │ │ ├── StubPendingActivity.java
│ │ │ │ ├── StubPendingReceiver.java
│ │ │ │ ├── StubPendingService.java
│ │ │ │ └── VASettings.java
│ │ │ ├── helper/
│ │ │ │ ├── ArtDexOptimizer.java
│ │ │ │ ├── ParcelHelper.java
│ │ │ │ ├── PersistenceLayer.java
│ │ │ │ ├── collection/
│ │ │ │ │ ├── ArrayMap.java
│ │ │ │ │ ├── ArraySet.java
│ │ │ │ │ ├── ContainerHelpers.java
│ │ │ │ │ ├── IntArray.java
│ │ │ │ │ ├── MapCollections.java
│ │ │ │ │ ├── SimpleArrayMap.java
│ │ │ │ │ └── SparseArray.java
│ │ │ │ ├── compat/
│ │ │ │ │ ├── AccountManagerCompat.java
│ │ │ │ │ ├── ActivityManagerCompat.java
│ │ │ │ │ ├── ApplicationThreadCompat.java
│ │ │ │ │ ├── BuildCompat.java
│ │ │ │ │ ├── BundleCompat.java
│ │ │ │ │ ├── ContentProviderCompat.java
│ │ │ │ │ ├── ContentResolverCompat.java
│ │ │ │ │ ├── IApplicationThreadCompat.java
│ │ │ │ │ ├── NativeLibraryHelperCompat.java
│ │ │ │ │ ├── ObjectsCompat.java
│ │ │ │ │ ├── PackageParserCompat.java
│ │ │ │ │ ├── ParceledListSliceCompat.java
│ │ │ │ │ ├── StorageManagerCompat.java
│ │ │ │ │ └── SystemPropertiesCompat.java
│ │ │ │ └── utils/
│ │ │ │ ├── ArrayUtils.java
│ │ │ │ ├── AtomicFile.java
│ │ │ │ ├── BitmapUtils.java
│ │ │ │ ├── ClassUtils.java
│ │ │ │ ├── ComponentUtils.java
│ │ │ │ ├── DeviceUtil.java
│ │ │ │ ├── DrawableUtils.java
│ │ │ │ ├── EncodeUtils.java
│ │ │ │ ├── FastXmlSerializer.java
│ │ │ │ ├── FileUtils.java
│ │ │ │ ├── MD5Utils.java
│ │ │ │ ├── OSUtils.java
│ │ │ │ ├── Reflect.java
│ │ │ │ ├── ReflectException.java
│ │ │ │ ├── SchedulerTask.java
│ │ │ │ ├── Singleton.java
│ │ │ │ ├── VLog.java
│ │ │ │ ├── XmlSerializerAndParser.java
│ │ │ │ └── marks/
│ │ │ │ ├── FakeDeviceMark.java
│ │ │ │ └── FakeLocMark.java
│ │ │ ├── os/
│ │ │ │ ├── VBinder.java
│ │ │ │ ├── VEnvironment.java
│ │ │ │ ├── VUserHandle.java
│ │ │ │ ├── VUserInfo.java
│ │ │ │ └── VUserManager.java
│ │ │ ├── remote/
│ │ │ │ ├── AppTaskInfo.java
│ │ │ │ ├── BadgerInfo.java
│ │ │ │ ├── InstallResult.java
│ │ │ │ ├── InstalledAppInfo.java
│ │ │ │ ├── PendingIntentData.java
│ │ │ │ ├── PendingResultData.java
│ │ │ │ ├── Problem.java
│ │ │ │ ├── ReceiverInfo.java
│ │ │ │ ├── StubActivityRecord.java
│ │ │ │ ├── SyncInfo.java
│ │ │ │ ├── VDeviceInfo.java
│ │ │ │ ├── VParceledListSlice.java
│ │ │ │ └── vloc/
│ │ │ │ ├── VCell.java
│ │ │ │ ├── VLocation.java
│ │ │ │ └── VWifi.java
│ │ │ └── server/
│ │ │ ├── BinderProvider.java
│ │ │ ├── IJobScheduler.java
│ │ │ ├── ServiceCache.java
│ │ │ ├── accounts/
│ │ │ │ ├── RegisteredServicesParser.java
│ │ │ │ ├── VAccount.java
│ │ │ │ ├── VAccountManagerService.java
│ │ │ │ ├── VContentService.java
│ │ │ │ └── VSyncRecord.java
│ │ │ ├── am/
│ │ │ │ ├── ActivityRecord.java
│ │ │ │ ├── ActivityStack.java
│ │ │ │ ├── AppBindRecord.java
│ │ │ │ ├── AttributeCache.java
│ │ │ │ ├── BroadcastSystem.java
│ │ │ │ ├── ConnectionRecord.java
│ │ │ │ ├── PendingIntents.java
│ │ │ │ ├── ProcessMap.java
│ │ │ │ ├── ProcessRecord.java
│ │ │ │ ├── ServiceRecord.java
│ │ │ │ ├── TaskRecord.java
│ │ │ │ ├── UidSystem.java
│ │ │ │ └── VActivityManagerService.java
│ │ │ ├── device/
│ │ │ │ ├── DeviceInfoPersistenceLayer.java
│ │ │ │ └── VDeviceManagerService.java
│ │ │ ├── job/
│ │ │ │ └── VJobSchedulerService.java
│ │ │ ├── location/
│ │ │ │ └── VirtualLocationService.java
│ │ │ ├── notification/
│ │ │ │ ├── NotificationCompat.java
│ │ │ │ ├── NotificationCompatCompatV14.java
│ │ │ │ ├── NotificationCompatCompatV21.java
│ │ │ │ ├── NotificationFixer.java
│ │ │ │ ├── PendIntentCompat.java
│ │ │ │ ├── ReflectionActionCompat.java
│ │ │ │ ├── RemoteViewsFixer.java
│ │ │ │ ├── VNotificationManagerService.java
│ │ │ │ └── WidthCompat.java
│ │ │ ├── pm/
│ │ │ │ ├── FastImmutableArraySet.java
│ │ │ │ ├── IntentResolver.java
│ │ │ │ ├── PackageCacheManager.java
│ │ │ │ ├── PackagePersistenceLayer.java
│ │ │ │ ├── PackageSetting.java
│ │ │ │ ├── PackageUserState.java
│ │ │ │ ├── PrivilegeAppOptimizer.java
│ │ │ │ ├── ProviderIntentResolver.java
│ │ │ │ ├── VAppManagerService.java
│ │ │ │ ├── VPackageManagerService.java
│ │ │ │ ├── VUserManagerService.java
│ │ │ │ ├── installer/
│ │ │ │ │ ├── FileBridge.java
│ │ │ │ │ ├── PackageHelper.java
│ │ │ │ │ ├── PackageInstallInfo.java
│ │ │ │ │ ├── PackageInstallObserver.java
│ │ │ │ │ ├── PackageInstallerSession.java
│ │ │ │ │ ├── SessionInfo.java
│ │ │ │ │ ├── SessionParams.java
│ │ │ │ │ └── VPackageInstallerService.java
│ │ │ │ └── parser/
│ │ │ │ ├── PackageParserEx.java
│ │ │ │ └── VPackage.java
│ │ │ ├── secondary/
│ │ │ │ ├── BinderDelegateService.java
│ │ │ │ └── FakeIdentityBinder.java
│ │ │ └── vs/
│ │ │ ├── VSConfig.java
│ │ │ ├── VSPersistenceLayer.java
│ │ │ └── VirtualStorageService.java
│ │ └── mirror/
│ │ ├── MethodParams.java
│ │ ├── MethodReflectParams.java
│ │ ├── RefBoolean.java
│ │ ├── RefClass.java
│ │ ├── RefConstructor.java
│ │ ├── RefDouble.java
│ │ ├── RefFloat.java
│ │ ├── RefInt.java
│ │ ├── RefLong.java
│ │ ├── RefMethod.java
│ │ ├── RefObject.java
│ │ ├── RefStaticInt.java
│ │ ├── RefStaticMethod.java
│ │ ├── RefStaticObject.java
│ │ ├── android/
│ │ │ ├── accounts/
│ │ │ │ └── IAccountManager.java
│ │ │ ├── app/
│ │ │ │ ├── Activity.java
│ │ │ │ ├── ActivityManagerNative.java
│ │ │ │ ├── ActivityManagerOreo.java
│ │ │ │ ├── ActivityThread.java
│ │ │ │ ├── ActivityThreadNMR1.java
│ │ │ │ ├── ApplicationThreadNative.java
│ │ │ │ ├── ContextImpl.java
│ │ │ │ ├── ContextImplICS.java
│ │ │ │ ├── ContextImplKitkat.java
│ │ │ │ ├── IActivityManager.java
│ │ │ │ ├── IActivityManagerICS.java
│ │ │ │ ├── IActivityManagerL.java
│ │ │ │ ├── IActivityManagerN.java
│ │ │ │ ├── IActivityTaskManager.java
│ │ │ │ ├── IAlarmManager.java
│ │ │ │ ├── IApplicationThread.java
│ │ │ │ ├── IApplicationThreadICSMR1.java
│ │ │ │ ├── IApplicationThreadJBMR1.java
│ │ │ │ ├── IApplicationThreadKitkat.java
│ │ │ │ ├── IApplicationThreadOreo.java
│ │ │ │ ├── ISearchManager.java
│ │ │ │ ├── IServiceConnectionO.java
│ │ │ │ ├── IUsageStatsManager.java
│ │ │ │ ├── LoadedApk.java
│ │ │ │ ├── LoadedApkHuaWei.java
│ │ │ │ ├── Notification.java
│ │ │ │ ├── NotificationL.java
│ │ │ │ ├── NotificationM.java
│ │ │ │ ├── NotificationManager.java
│ │ │ │ ├── PendingIntentJBMR2.java
│ │ │ │ ├── ServiceStartArgs.java
│ │ │ │ ├── admin/
│ │ │ │ │ └── IDevicePolicyManager.java
│ │ │ │ ├── backup/
│ │ │ │ │ └── IBackupManager.java
│ │ │ │ └── job/
│ │ │ │ ├── IJobScheduler.java
│ │ │ │ ├── JobInfo.java
│ │ │ │ ├── JobParameters.java
│ │ │ │ └── JobWorkItem.java
│ │ │ ├── bluetooth/
│ │ │ │ └── IBluetooth.java
│ │ │ ├── content/
│ │ │ │ ├── BroadcastReceiver.java
│ │ │ │ ├── ClipboardManager.java
│ │ │ │ ├── ClipboardManagerOreo.java
│ │ │ │ ├── ContentProviderClient.java
│ │ │ │ ├── ContentProviderHolderOreo.java
│ │ │ │ ├── ContentProviderNative.java
│ │ │ │ ├── ContentResolver.java
│ │ │ │ ├── ContentResolverJBMR2.java
│ │ │ │ ├── IClipboard.java
│ │ │ │ ├── IContentProvider.java
│ │ │ │ ├── IContentService.java
│ │ │ │ ├── IIntentReceiver.java
│ │ │ │ ├── IIntentReceiverJB.java
│ │ │ │ ├── IRestrictionsManager.java
│ │ │ │ ├── IntentFilter.java
│ │ │ │ ├── SyncAdapterType.java
│ │ │ │ ├── SyncAdapterTypeN.java
│ │ │ │ ├── SyncInfo.java
│ │ │ │ ├── SyncRequest.java
│ │ │ │ ├── pm/
│ │ │ │ │ ├── ApplicationInfoL.java
│ │ │ │ │ ├── ApplicationInfoN.java
│ │ │ │ │ ├── ILauncherApps.java
│ │ │ │ │ ├── IShortcutService.java
│ │ │ │ │ ├── LauncherApps.java
│ │ │ │ │ ├── PackageInstaller.java
│ │ │ │ │ ├── PackageParser.java
│ │ │ │ │ ├── PackageParserJellyBean.java
│ │ │ │ │ ├── PackageParserJellyBean17.java
│ │ │ │ │ ├── PackageParserLollipop.java
│ │ │ │ │ ├── PackageParserLollipop22.java
│ │ │ │ │ ├── PackageParserMarshmallow.java
│ │ │ │ │ ├── PackageParserNougat.java
│ │ │ │ │ ├── PackageParserP28.java
│ │ │ │ │ ├── PackageUserState.java
│ │ │ │ │ ├── ParceledListSlice.java
│ │ │ │ │ ├── ParceledListSliceJBMR2.java
│ │ │ │ │ ├── ShortcutInfo.java
│ │ │ │ │ └── UserInfo.java
│ │ │ │ └── res/
│ │ │ │ ├── AssetManager.java
│ │ │ │ └── CompatibilityInfo.java
│ │ │ ├── ddm/
│ │ │ │ ├── DdmHandleAppName.java
│ │ │ │ └── DdmHandleAppNameJBMR1.java
│ │ │ ├── graphics/
│ │ │ │ └── drawable/
│ │ │ │ └── Icon.java
│ │ │ ├── hardware/
│ │ │ │ ├── display/
│ │ │ │ │ ├── DisplayManagerGlobal.java
│ │ │ │ │ └── IDisplayManager.java
│ │ │ │ ├── fingerprint/
│ │ │ │ │ └── IFingerprintService.java
│ │ │ │ └── location/
│ │ │ │ └── IContextHubService.java
│ │ │ ├── location/
│ │ │ │ ├── ILocationListener.java
│ │ │ │ ├── ILocationManager.java
│ │ │ │ ├── LocationManager.java
│ │ │ │ └── LocationRequestL.java
│ │ │ ├── media/
│ │ │ │ ├── AudioManager.java
│ │ │ │ ├── IAudioService.java
│ │ │ │ ├── IMediaRouterService.java
│ │ │ │ ├── MediaRouter.java
│ │ │ │ └── session/
│ │ │ │ └── ISessionManager.java
│ │ │ ├── net/
│ │ │ │ ├── IConnectivityManager.java
│ │ │ │ ├── NetworkInfo.java
│ │ │ │ └── wifi/
│ │ │ │ ├── IWifiManager.java
│ │ │ │ ├── WifiInfo.java
│ │ │ │ ├── WifiScanner.java
│ │ │ │ └── WifiSsid.java
│ │ │ ├── os/
│ │ │ │ ├── BaseBundle.java
│ │ │ │ ├── Build.java
│ │ │ │ ├── Bundle.java
│ │ │ │ ├── BundleICS.java
│ │ │ │ ├── Handler.java
│ │ │ │ ├── IDeviceIdentifiersPolicyService.java
│ │ │ │ ├── INetworkManagementService.java
│ │ │ │ ├── IPowerManager.java
│ │ │ │ ├── IUserManager.java
│ │ │ │ ├── Message.java
│ │ │ │ ├── Process.java
│ │ │ │ ├── ServiceManager.java
│ │ │ │ ├── StrictMode.java
│ │ │ │ ├── UserHandle.java
│ │ │ │ ├── mount/
│ │ │ │ │ └── IMountService.java
│ │ │ │ └── storage/
│ │ │ │ └── IStorageManager.java
│ │ │ ├── providers/
│ │ │ │ └── Settings.java
│ │ │ ├── renderscript/
│ │ │ │ └── RenderScriptCacheDir.java
│ │ │ ├── rms/
│ │ │ │ └── resource/
│ │ │ │ ├── ReceiverResourceLP.java
│ │ │ │ ├── ReceiverResourceM.java
│ │ │ │ └── ReceiverResourceN.java
│ │ │ ├── service/
│ │ │ │ └── persistentdata/
│ │ │ │ └── IPersistentDataBlockService.java
│ │ │ ├── telephony/
│ │ │ │ ├── CellIdentityCdma.java
│ │ │ │ ├── CellIdentityGsm.java
│ │ │ │ ├── CellInfoCdma.java
│ │ │ │ ├── CellInfoGsm.java
│ │ │ │ ├── CellSignalStrengthCdma.java
│ │ │ │ ├── CellSignalStrengthGsm.java
│ │ │ │ └── NeighboringCellInfo.java
│ │ │ ├── util/
│ │ │ │ └── Singleton.java
│ │ │ ├── view/
│ │ │ │ ├── Display.java
│ │ │ │ ├── HardwareRenderer.java
│ │ │ │ ├── IAutoFillManager.java
│ │ │ │ ├── IGraphicsStats.java
│ │ │ │ ├── IWindowManager.java
│ │ │ │ ├── RenderScript.java
│ │ │ │ ├── SurfaceControl.java
│ │ │ │ ├── ThreadedRenderer.java
│ │ │ │ └── WindowManagerGlobal.java
│ │ │ ├── webkit/
│ │ │ │ ├── IWebViewUpdateService.java
│ │ │ │ └── WebViewFactory.java
│ │ │ └── widget/
│ │ │ ├── RemoteViews.java
│ │ │ └── Toast.java
│ │ ├── com/
│ │ │ └── android/
│ │ │ └── internal/
│ │ │ ├── R_Hide.java
│ │ │ ├── app/
│ │ │ │ ├── IAppOpsService.java
│ │ │ │ └── IBatteryStats.java
│ │ │ ├── appwidget/
│ │ │ │ └── IAppWidgetService.java
│ │ │ ├── content/
│ │ │ │ ├── NativeLibraryHelper.java
│ │ │ │ └── ReferrerIntent.java
│ │ │ ├── os/
│ │ │ │ ├── IDropBoxManagerService.java
│ │ │ │ ├── IVibratorService.java
│ │ │ │ ├── UserManager.java
│ │ │ │ └── health/
│ │ │ │ └── SystemHealthManager.java
│ │ │ ├── policy/
│ │ │ │ └── PhoneWindow.java
│ │ │ ├── telephony/
│ │ │ │ ├── IMms.java
│ │ │ │ ├── IPhoneSubInfo.java
│ │ │ │ ├── ISms.java
│ │ │ │ ├── ISub.java
│ │ │ │ ├── ITelephony.java
│ │ │ │ ├── ITelephonyRegistry.java
│ │ │ │ └── PhoneConstantsMtk.java
│ │ │ └── view/
│ │ │ ├── IInputMethodManager.java
│ │ │ └── inputmethod/
│ │ │ └── InputMethodManager.java
│ │ ├── dalvik/
│ │ │ └── system/
│ │ │ └── VMRuntime.java
│ │ ├── java/
│ │ │ └── lang/
│ │ │ ├── ThreadGroup.java
│ │ │ └── ThreadGroupN.java
│ │ ├── libcore/
│ │ │ └── io/
│ │ │ ├── ForwardingOs.java
│ │ │ ├── Libcore.java
│ │ │ └── Os.java
│ │ └── mirror/
│ │ └── android/
│ │ └── security/
│ │ └── net/
│ │ └── config/
│ │ └── ApplicationConfig.java
│ ├── jni/
│ │ ├── A64Inlinehook/
│ │ │ ├── And64InlineHook.cpp
│ │ │ └── And64InlineHook.hpp
│ │ ├── Android.mk
│ │ ├── Application.mk
│ │ ├── Foundation/
│ │ │ ├── IOUniformer.cpp
│ │ │ ├── IOUniformer.h
│ │ │ ├── Path.cpp
│ │ │ ├── Path.h
│ │ │ ├── SandboxFs.cpp
│ │ │ ├── SandboxFs.h
│ │ │ ├── SymbolFinder.cpp
│ │ │ ├── SymbolFinder.h
│ │ │ ├── VMPatch.cpp
│ │ │ ├── VMPatch.h
│ │ │ ├── fake_dlfcn.cpp
│ │ │ └── fake_dlfcn.h
│ │ ├── Jni/
│ │ │ ├── Helper.h
│ │ │ ├── VAJni.cpp
│ │ │ └── VAJni.h
│ │ ├── Substrate/
│ │ │ ├── Buffer.hpp
│ │ │ ├── CydiaSubstrate.h
│ │ │ ├── SubstrateARM.hpp
│ │ │ ├── SubstrateDebug.cpp
│ │ │ ├── SubstrateDebug.hpp
│ │ │ ├── SubstrateHook.cpp
│ │ │ ├── SubstrateHook.h
│ │ │ ├── SubstrateLog.hpp
│ │ │ ├── SubstratePosixMemory.cpp
│ │ │ ├── SubstrateX86.hpp
│ │ │ ├── hde64.c
│ │ │ ├── hde64.h
│ │ │ └── table64.h
│ │ └── fb/
│ │ ├── Android.mk
│ │ ├── BUCK
│ │ ├── Doxyfile
│ │ ├── assert.cpp
│ │ ├── include/
│ │ │ ├── fb/
│ │ │ │ ├── ALog.h
│ │ │ │ ├── Build.h
│ │ │ │ ├── Countable.h
│ │ │ │ ├── Doxyfile
│ │ │ │ ├── Environment.h
│ │ │ │ ├── ProgramLocation.h
│ │ │ │ ├── RefPtr.h
│ │ │ │ ├── StaticInitialized.h
│ │ │ │ ├── ThreadLocal.h
│ │ │ │ ├── assert.h
│ │ │ │ ├── fbjni/
│ │ │ │ │ ├── Boxed.h
│ │ │ │ │ ├── ByteBuffer.h
│ │ │ │ │ ├── Common.h
│ │ │ │ │ ├── Context.h
│ │ │ │ │ ├── CoreClasses-inl.h
│ │ │ │ │ ├── CoreClasses.h
│ │ │ │ │ ├── Exceptions.h
│ │ │ │ │ ├── File.h
│ │ │ │ │ ├── Hybrid.h
│ │ │ │ │ ├── Iterator-inl.h
│ │ │ │ │ ├── Iterator.h
│ │ │ │ │ ├── JThread.h
│ │ │ │ │ ├── JWeakReference.h
│ │ │ │ │ ├── Meta-forward.h
│ │ │ │ │ ├── Meta-inl.h
│ │ │ │ │ ├── Meta.h
│ │ │ │ │ ├── MetaConvert.h
│ │ │ │ │ ├── NativeRunnable.h
│ │ │ │ │ ├── ReferenceAllocators-inl.h
│ │ │ │ │ ├── ReferenceAllocators.h
│ │ │ │ │ ├── References-forward.h
│ │ │ │ │ ├── References-inl.h
│ │ │ │ │ ├── References.h
│ │ │ │ │ ├── Registration-inl.h
│ │ │ │ │ ├── Registration.h
│ │ │ │ │ └── TypeTraits.h
│ │ │ │ ├── fbjni.h
│ │ │ │ ├── log.h
│ │ │ │ ├── lyra.h
│ │ │ │ ├── noncopyable.h
│ │ │ │ ├── nonmovable.h
│ │ │ │ └── visibility.h
│ │ │ └── jni/
│ │ │ ├── Countable.h
│ │ │ ├── GlobalReference.h
│ │ │ ├── JniTerminateHandler.h
│ │ │ ├── LocalReference.h
│ │ │ ├── LocalString.h
│ │ │ ├── Registration.h
│ │ │ ├── WeakReference.h
│ │ │ └── jni_helpers.h
│ │ ├── jni/
│ │ │ ├── ByteBuffer.cpp
│ │ │ ├── Countable.cpp
│ │ │ ├── Environment.cpp
│ │ │ ├── Exceptions.cpp
│ │ │ ├── Hybrid.cpp
│ │ │ ├── LocalString.cpp
│ │ │ ├── OnLoad.cpp
│ │ │ ├── References.cpp
│ │ │ ├── WeakReference.cpp
│ │ │ ├── android/
│ │ │ │ ├── CpuCapabilities.cpp
│ │ │ │ └── ReferenceChecking.cpp
│ │ │ ├── fbjni.cpp
│ │ │ ├── java/
│ │ │ │ ├── BUCK
│ │ │ │ ├── CppException.java
│ │ │ │ ├── CppSystemErrorException.java
│ │ │ │ └── UnknownCppException.java
│ │ │ └── jni_helpers.cpp
│ │ ├── log.cpp
│ │ ├── lyra/
│ │ │ └── lyra.cpp
│ │ └── onload.cpp
│ └── res/
│ ├── layout/
│ │ ├── app_not_authorized.xml
│ │ ├── choose_account_row.xml
│ │ ├── choose_account_type.xml
│ │ ├── choose_type_and_account.xml
│ │ ├── custom_notification.xml
│ │ ├── custom_notification_lite.xml
│ │ └── resolve_list_item.xml
│ ├── values/
│ │ ├── dimens.xml
│ │ ├── integer.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ ├── values-ru/
│ │ └── strings.xml
│ └── values-uk/
│ └── strings.xml
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# 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
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report_cn.md
================================================
---
name: BUG反馈
about: 中文BUG反馈
---
**反馈BUG之前,先issue里面搜看看有没有别人已经反馈过,重复的不予处理!!**
## 问题描述
(请尽量详细地描述你遇到的问题)
## 复现步骤
(请分步骤描述如何复现这个BUG,非毕现BUG请给出如何能大概率复现的步骤)
## 环境
机型:
系统版本:
ROM版本:(请区分内测版和开发版稳定版,除稳定版本外不予修复)
Xposed 插件以及插件版本:
VirtualXposed版本:
## 补充
(别的需要描述的内容)
**写完之后,请自己再读一遍自己写的,如果你自己都读不懂,就不用说修复了**
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request_cn.md
================================================
---
name: 意见和建议
about: Feature中文版
---
**BUG反馈请不要用这个模版,否则直接关闭!!**
## 场景描述
(请详细和精确地表述你的使用场景)
## 希望的解决方案
(你希望如何解决这个问题?)
## 其他信息
(其他你认为有用的信息)
================================================
FILE: .github/issue-close-app.yml
================================================
# Comment that will be sent if an issue is judged to be closed
comment: "This issue is closed because it does not meet our issue template/为方便解决问题,请使用 issue 模版提交问题。"
issueConfigs:
# There can be several configs for different kind of issues.
- content:
# Example 1: bug report
- "Expected behavior"
- "To Reproduce"
- "Describe the bug"
- content:
# Example 2: feature request
- "Describe the solution you'd like"
- content:
- "问题描述"
- "复现步骤"
- "环境"
- content:
- "场景描述"
- "希望的解决方案"
================================================
FILE: .github/stale.yml
================================================
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 20
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- bug
- security
# Label to use when marking an issue as stale
staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
================================================
FILE: .github/workflows/android.yml
================================================
name: Android CI
on: [pull_request, push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Checkout submodules
uses: srt32/git-actions@v0.0.3
with:
args: git submodule update --init --recursive
- name: set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build with Gradle
run: cd VirtualApp && ./gradlew assembleRelease
- name: ls
run: ls
- name: Archive production artifacts
uses: actions/upload-artifact@v1
with:
name: compiled
path: VirtualApp/app/build/
================================================
FILE: .gitignore
================================================
# Built application files
*.apk
*.ap_
# Files for the ART/Dalvik VM
*.dex
.idea
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# Intellij
*.iml
.idea/workspace.xml
# Keystore files
*.jks
================================================
FILE: .gitmodules
================================================
[submodule "VirtualApp/launcher"]
path = VirtualApp/launcher
url = https://github.com/android-hacker/Launcher3.git
================================================
FILE: .travis.yml
================================================
language: android
android:
components:
- tools
- build-tools-28.0.3
- android-27
- android-28
- extra-android-m2repository
- extra-android-support
install:
- echo y | sdkmanager 'ndk-bundle'
- echo y | sdkmanager 'cmake;3.6.4111459'
- echo y | sdkmanager 'lldb;3.0'
before_install:
- chmod +x ./VirtualApp/gradlew
script:
- cd VirtualApp
- ./gradlew assembleRelease
================================================
FILE: CHINESE.md
================================================
[](https://travis-ci.org/android-hacker/VirtualXposed)
简介
-----
**VirtualXposed** 是基于[VirtualApp](https://github.com/asLody/VirtualApp) 和 [epic](https://github.com/tiann/epic) 在**非ROOT**环境下运行Xposed模块的实现(支持5.0~10.0)。
与 Xposed 相比,目前 VirtualXposed 有两个限制:
1. 不支持修改系统(可以修改普通APP中对系统API的调用),因此重力工具箱,应用控制器等无法使用。
2. 暂不支持资源HOOK,因此资源钩子不会起任何作用;使用资源HOOK的模块,相应的功能不会生效。
警告
-------
本项目使用的 VirtualApp 不允许用于商业用途,并且其内部的 VirtualApp 版本已经过时,如果有这个需求,为了贵公司的长期稳定发展,请使用商业授权,联系 Lody (imlody@foxmail.com)即可。
使用
----------
## 准备
首先在 [发布页面](https://github.com/android-hacker/VirtualXposed/releases) 下载最新的VAExposed安装包安装到手机。
## 安装模块
打开 VirtualXposed,在里面安装要使用的APP,以及相应的Xposed模块即可。
注意:**所有的工作(安装Xposed模块,安装APP)必须在 VirtualXposed中**进行,否则Xposed模块不会有任何作用!比如,将微信直接安装在系统上(而非VirtualXposed中),防撤回安装在VirtualXposed中;或者把微信安装在VirtualXposed上,防撤回插件直接安装在系统上;或者两者都直接安装在系统上,**均不会起任何作用**。
在VirtualXposed中安装App有两种方式:
1. 直接复制已经在系统中安装好的APP,比如如果你系统中装了微信,那么可以直接复制一份。
2. 通过外置存储直接安装APK文件;点主界面的底部按钮-添加应用,然后选择后面两个TAB即可。
在VirtualXposed中安装Xposed模块,可以跟安装正常的APK一样,以上两种安装App的方式也适用于安装Xposed模块。不过,你也可以通过VirtualXposed中内置的XposedInstaller来安装和管理模块,跟通常的XposedInstaller使用方式一样;去下载页面,下载安装即可。
## 亲测可用的模块
- [XPrivacyLua][xpl]: Really simple to use privacy manager for Android 6.0 Marshmallow and later.
- [XInsta][xinsta]: Instagram module(Feed downing, stories downloading, etc).
- [Minminguard][minminguard]: Completely remove both the ads inside apps and the empty space caused by those ads.
- [YouTube AdAway][yta]: Get rid of ads on the official YouTube App.
- [微X模块][wx]: 微信模块,功能强大。
- [畅玩微信][cwwx]: 微信模块新秀,功能丰富。
- [微信巫师][wxws]: 微信模块,项目开源,代码优秀。
- [MDWechat][mdwechat]: 微信美化模块,可以把微信整成MD风格。
- [应用变量][yybl]: 可以用来进行机型修改,比如王者荣耀高帧率;QQ空间修改小尾巴等。
- [音量增强器][ylzqq]: 网易云音乐模块,非常好用,低调。
- [微信学英语][wxxyy]: 自动把微信消息翻译为英语,非常实用。
- [情迁抢包][qqqb]: 微信QQ抢红包模块。
- [微信跳一跳助手][ttzs]: 微信跳一跳游戏辅助模块。
- [步数修改器][bsxg]: 运动步数修改模块。
- [模拟位置][mnwz]: 虚拟定位模块,稳定好用。
- [指纹支付][zwzf]: 对不支持指纹支付但系统本身有指纹的手机开启指纹支付的模块。
- [QQ精简模块 2.0][qqjj]: QQ模块,不仅可以精简QQ,还能防撤回,防闪照。
- [微信增强插件][wxzqcj]: 微信模块,VXP内最稳定的微信模块;如无特殊需求建议用这个。
- [QX模块][qx]: QQ模块,防撤回抢红包斗图一应俱全。
- [QQ斗图神器][qqdtsq]: 各种表情,斗图神器。
- [微信斗图神器][wxdtsq]: 斗图神器,微信用的。
- [大圣净化][dsjh]: 去广告神器,推荐使用。
真正能用的模块远不止这么多,要用的话可以自己测试;如果你发现某些模块可以用但不在上面的列表中,欢迎给我发个PR。
其他
-------
### GameGuardian
VirtualXposed也支持GG修改器,如果你需要用GG,那么请使用GG专版(可以在发布页面下载,带 For_GameGuardian后缀)。
[GG修改器使用视频教程](https://gameguardian.net/forum/gallery/image/437-no-root-via-virtualxposed-without-error-105-gameguardian/)
### VirusTotal
VirusTotal 还有一些其他的杀毒引擎检测到VirtualXposed有病毒,这一点我该不承认,而且我觉得这些愚蠢的杀毒引擎是在胡扯。请看[我的说明](https://github.com/android-hacker/VirtualXposed/issues/10).
而且,VirtualXposed是开源的,你可以直接查看代码;我可以打包票,VirtualXposed本身没有做任何有害的事情(但是它确实有这个能力,所以请不要下载不明来源的Xposed插件)。
如果你还是不放心,那么你可以使用 [0.8.7版本](https://github.com/android-hacker/VirtualXposed/releases/tag/0.8.7), 这个版本杀毒引擎的检测结果是安全的(简直就是扯淡)。
支持和加入
------------
目前VirtualXposed 还不完善,如果你对非ROOT下实现Xposed感兴趣;欢迎加入!你可以通过如下方式来支持:
1. 直接贡献代码,提供Feature,修复BUG!
2. 使用你拥有的手机,安装你常用的Xposed模块,反馈不可用情况;协助帮忙解决兼容性问题!
3. 提出体验上,功能上的建议,帮助完善VirtualXposed!
致谢
------
1. [VirtualApp](https://github.com/asLody/VirtualApp)
2. [Xposed](https://github.com/rovo89/Xposed)
[wx]: http://repo.xposed.info/module/com.fkzhang.wechatxposed
[qx]: http://repo.xposed.info/module/com.fkzhang.qqxposed
[wxws]: https://github.com/Gh0u1L5/WechatMagician/releases
[yybl]: https://www.coolapk.com/apk/com.sollyu.xposed.hook.model
[ylzqq]: https://github.com/bin456789/Unblock163MusicClient-Xposed/releases
[wxxyy]: https://www.coolapk.com/apk/com.hiwechart.translate
[qqqb]: http://repo.xposed.info/module/cn.qssq666.redpacket
[ttzs]: http://repo.xposed.info/module/com.emily.mmjumphelper
[mnwz]: https://www.coolapk.com/apk/com.rong.xposed.fakelocation
[zwzf]: https://github.com/android-hacker/Xposed-Fingerprint-pay/releases
[bsxg]: https://www.coolapk.com/apk/com.specher.sm
[mdwechat]: https://github.com/Blankeer/MDWechat
[wxzqcj]:https://github.com/firesunCN/WechatEnhancement
[qqjj]: https://www.coolapk.com/apk/me.zpp0196.qqsimple
[qqdtsq]: https://www.coolapk.com/apk/x.hook.qqemoji
[wxdtsq]: https://www.coolapk.com/apk/x.hook.emojihook
[dsjh]: https://wiki.ad-gone.com/archives/32
[xpl]: https://github.com/android-hacker/VirtualXposed/wiki/Privacy-control(XPrivacyLua)
[minminguard]: http://repo.xposed.info/module/tw.fatminmin.xposed.minminguard
[yta]: http://repo.xposed.info/module/ma.wanam.youtubeadaway
[xinsta]: http://repo.xposed.info/module/com.ihelp101.instagram
[cwwx]: http://repo.xposed.info/module/com.example.wx_plug_in3
================================================
FILE: LICENSE.txt
================================================
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Copyright (C)
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
.
================================================
FILE: README.md
================================================
[](https://travis-ci.org/android-hacker/VirtualXposed)
[中文文档](CHINESE.md "中文")
Introduction
------------
**VirtualXposed** is a simple App based on [VirtualApp](https://github.com/asLody/VirtualApp) and [epic](https://github.com/tiann/epic) that allows you to use an Xposed Module without needing to root, unlock the bootloader, or flash a custom system image. (Supports Android 5.0~10.0)
The only two restriction of VirtualXposed are:
1. Unable to modify system, so any Module which modifies system won't be able to work properly.
2. Currently resource hooks are not supported. (Theming modules use Resource Hooks).
Warning
-----------
Usage for Commercial Purposes are not allowed!!! Please refer to VirtualApp's [declaration](https://github.com/asLody/VirtualApp).
Usage
-------
### Preparation
Download the latest APK from the [release page](https://github.com/android-hacker/VirtualXposed/releases), and install it on your Android device.
### Install APP and Xposed Module
Open VirtualXposed, Click on the **Drawer Button** at the bottom of home page(Or long click the screen), add your desired APP and Xposed Module to VirtualXposed's virtual environment.
Note: **All operations(installation of Xposed Module, APP)must be done in VirtualXposed**, otherwise the Xposed Module installed won't take effect. For example, if you install the YouTube app on your system (Your phone's original system, not in VirtualXposed), and then install YouTube AdAway (A YouTube Xposed Module) in VirtualXposed; or you install YouTube in VirtualXposed, and install YouTube AdAway on original system; or both of them are installed on original system, **neither of these three cases will work!**

There are three ways to install an APP or Xposed Module to VirtualXposed:
1. **Clone an installed app from your original system.** (Click Button at bottom of home page, then click Add App, the first page shows a list of installed apps.)
2. **Install via an APK file.** (Click Button at bottom of home page, then click Add App, the second page shows APKs found in your sdcard)
3. **Install via an external file chooser.** (Click Button at bottom of home page, then click Add App, use the floating action button to choose an APK file to install)
For Xposed Module, You can install it from Xposed Installer, too.
### Activate the Xposed Module
Open Xposed Installer in VirtualXposed, go to the module fragment, check the module you want to use:

### Reboot
You only need to reboot VirtualXposed, **There's no need to reboot your phone**; Just click Settings in home page of VirtualXposed, click `Reboot` button, and VirtualXposed will reboot in a blink.

Supported Modules
-------------------------
Almost all modules except system-relevant are supported, please try it by yourself :)
Others
-------
### GameGuardian
VirtualXposed also supports GameGuardian, **you should use the separate version for GameGuardian**.(Download it in release page).
[Video Tutorial](https://gameguardian.net/forum/gallery/image/437-no-root-via-virtualxposed-without-error-105-gameguardian/)
### VirusTotal
VirusTotal might report VirtualXposed as a malware, it is stupid, you can refer to my [explanation](https://github.com/android-hacker/VirtualXposed/issues/10).
And obviously, VirtualXposed is open source, so you can refer to the source code. I am sure that it is safe to use.
If you still couldn't believe in me, you can install version [0.8.7](https://github.com/android-hacker/VirtualXposed/releases/tag/0.8.7); VirusTotal reports this version as safe.
Support
-----------
Contributions to VirtualXposed are always welcomed!!
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
-------
1. [VirtualApp](https://github.com/asLody/VirtualApp)
2. [Xposed](https://github.com/rovo89/Xposed)
3. [And64InlineHook](https://github.com/Rprop/And64InlineHook)
================================================
FILE: VirtualApp/.gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.idea
.DS_Store
/build
/captures
================================================
FILE: VirtualApp/app/.gitignore
================================================
/build
================================================
FILE: VirtualApp/app/build.gradle
================================================
apply plugin: 'com.android.application'
Properties properties = new Properties()
def localProp = file(project.rootProject.file('local.properties'))
if (localProp.exists()) {
properties.load(localProp.newDataInputStream())
}
def keyFile = file(properties.getProperty("keystore.path") ?: "/tmp/does_not_exist")
android {
signingConfigs {
config {
keyAlias properties.getProperty("keystore.alias")
keyPassword properties.getProperty("keystore.pwd")
storeFile keyFile
storePassword properties.getProperty("keystore.alias_pwd")
}
}
compileSdkVersion 28
buildToolsVersion '28.0.3'
defaultConfig {
applicationId "io.va.exposed64"
minSdkVersion 21
targetSdkVersion 23
versionCode 220
versionName "0.22.0"
multiDexEnabled false
android {
defaultConfig {
ndk {
abiFilters "arm64-v8a", "x86_64"
}
}
}
}
// https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html?utm_source=android-studio
flavorDimensions 'main'
productFlavors {
aosp {
dimension 'main'
//matchingFallbacks ['aosp']
if (keyFile.exists()) {
signingConfig signingConfigs.config
}
}
fdroid {
dimension 'main'
}
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
buildTypes {
release {
minifyEnabled false
debuggable false
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
lintOptions {
checkReleaseBuilds false
// Or, if you prefer, you can continue to check for errors in release builds,
// but continue the build even when errors are found:
abortOnError false
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation project(':lib')
implementation project(':launcher')
//Android Lib
implementation 'com.android.support:multidex:1.0.3'
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:recyclerview-v7:27.1.1'
implementation 'com.android.support:design:27.1.1'
//Promise Support
implementation 'org.jdeferred:jdeferred-android-aar:1.2.4'
// ThirdParty
implementation 'com.jonathanfinerty.once:once:1.0.3'
def appCenterSdkVersion = '3.0.0'
aospImplementation("com.microsoft.appcenter:appcenter-analytics:${appCenterSdkVersion}")
aospImplementation("com.microsoft.appcenter:appcenter-crashes:${appCenterSdkVersion}")
implementation 'com.kyleduo.switchbutton:library:1.4.6'
implementation 'com.allenliu.versionchecklib:library:1.8.3'
implementation 'com.github.medyo:android-about-page:1.2.2'
implementation 'moe.feng:AlipayZeroSdk:1.1'
//Glide
implementation ('com.github.bumptech.glide:glide:4.8.0') {
exclude(group: "com.android.support")
}
annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'
}
================================================
FILE: VirtualApp/app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/lody/Desktop/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
-keep class com.amap.api.maps.**{*;}
-keep class com.autonavi.**{*;}
-keep class com.amap.api.trace.**{*;}
#定位
-keep class com.amap.api.location.**{*;}
-keep class com.amap.api.fence.**{*;}
-keep class com.autonavi.aps.amapapi.model.**{*;}
#搜索
-keep class com.amap.api.services.**{*;}
#2D地图
-keep class com.amap.api.maps2d.**{*;}
-keep class com.amap.api.mapcore2d.**{*;}
#导航
-keep class com.amap.api.navi.**{*;}
-keep class com.autonavi.**{*;}
##--Glide--
-keep class com.bumptech.glide.**{*;}
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
================================================
FILE: VirtualApp/app/src/aosp/java/io/virtualapp/delegate/MyCrashHandler.java
================================================
package io.virtualapp.delegate;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.util.Log;
import com.lody.virtual.client.VClientImpl;
import com.lody.virtual.client.core.VirtualCore;
import com.lody.virtual.os.VUserHandle;
import com.lody.virtual.remote.InstalledAppInfo;
import com.microsoft.appcenter.crashes.Crashes;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @author weishu
* @date 2019/2/25.
*/
public class MyCrashHandler extends BaseCrashHandler {
private static final String CRASH_SP = "vxp_crash";
private static final String KEY_LAST_CRASH_TIME = "last_crash_time";
private static final String KEY_LAST_CRASH_TYPE = "last_crash_type";
@Override
public void handleUncaughtException(Thread t, Throwable e) {
SharedPreferences sp = VirtualCore.get().getContext().getSharedPreferences(CRASH_SP, Context.MODE_MULTI_PROCESS);
Map properties = new HashMap<>();
try {
ApplicationInfo currentApplicationInfo = VClientImpl.get().getCurrentApplicationInfo();
if (currentApplicationInfo != null) {
String packageName = currentApplicationInfo.packageName;
String processName = currentApplicationInfo.processName;
properties.put("process", processName);
properties.put("package", packageName);
int userId = VUserHandle.myUserId();
properties.put("uid", String.valueOf(userId));
InstalledAppInfo installedAppInfo = VirtualCore.get().getInstalledAppInfo(packageName, 0);
if (installedAppInfo != null) {
PackageInfo packageInfo = installedAppInfo.getPackageInfo(userId);
if (packageInfo != null) {
String versionName = packageInfo.versionName;
int versionCode = packageInfo.versionCode;
properties.put("versionName", versionName);
properties.put("versionCode", String.valueOf(versionCode));
}
}
}
} catch (Throwable ignored) {
}
final String exceptionType = e.getClass().getName();
final long now = System.currentTimeMillis();
final long lastCrash = sp.getLong(KEY_LAST_CRASH_TIME, 0);
final String lastCrashType = sp.getString(KEY_LAST_CRASH_TYPE, null);
if (exceptionType.equals(lastCrashType) && (now - lastCrash) < TimeUnit.MINUTES.toMillis(1)) {
// continues crash, do not upload
} else {
Crashes.trackError(e, properties, null);
}
Log.i(TAG, "uncaught :" + t, e);
// must commit.
sp.edit().putLong(KEY_LAST_CRASH_TIME, now).putString(KEY_LAST_CRASH_TYPE, exceptionType).commit();
super.handleUncaughtException(t, e);
}
}
================================================
FILE: VirtualApp/app/src/aosp/java/io/virtualapp/delegate/MyVirtualInitializer.java
================================================
package io.virtualapp.delegate;
import android.app.Application;
import com.lody.virtual.client.core.VirtualCore;
import com.microsoft.appcenter.AppCenter;
import com.microsoft.appcenter.analytics.Analytics;
import com.microsoft.appcenter.crashes.Crashes;
/**
* @author weishu
* @date 2019/2/25.
*/
public class MyVirtualInitializer extends BaseVirtualInitializer {
public MyVirtualInitializer(Application application, VirtualCore core) {
super(application, core);
}
@Override
public void onMainProcess() {
AppCenter.start(application, "bf5e74bd-3795-49bd-95c8-327db494dd11",
Analytics.class, Crashes.class);
super.onMainProcess();
}
@Override
public void onVirtualProcess() {
// For Crash statics
AppCenter.start(application, "bf5e74bd-3795-49bd-95c8-327db494dd11",
Analytics.class, Crashes.class);
super.onVirtualProcess();
// Override
virtualCore.setCrashHandler(new MyCrashHandler());
}
}
================================================
FILE: VirtualApp/app/src/fdroid/java/io/virtualapp/delegate/MyVirtualInitializer.java
================================================
package io.virtualapp.delegate;
import android.app.Application;
import com.lody.virtual.client.core.VirtualCore;
/**
* @author weishu
* @date 2019/2/25.
*/
public class MyVirtualInitializer extends BaseVirtualInitializer {
public MyVirtualInitializer(Application application, VirtualCore core) {
super(application, core);
}
}
================================================
FILE: VirtualApp/app/src/main/AndroidManifest.xml
================================================
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/VCommends.java
================================================
package io.virtualapp;
/**
* @author Lody
*/
public class VCommends {
public static final String TAG_NEW_VERSION = "First launch new Version";
public static final String TAG_SHOW_ADD_APP_GUIDE = "Should show add app guide";
public static final int REQUEST_SELECT_APP = 5;
public static final String EXTRA_APP_INFO_LIST = "va.extra.APP_INFO_LIST";
public static final String TAG_ASK_INSTALL_GMS = "va.extra.ASK_INSTALL_GMS";
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/XApp.java
================================================
package io.virtualapp;
import android.app.Application;
import android.content.Context;
import android.os.Build;
import com.lody.virtual.client.NativeEngine;
import com.lody.virtual.client.core.VirtualCore;
import com.lody.virtual.client.stub.VASettings;
import io.virtualapp.delegate.MyVirtualInitializer;
/**
* @author Lody
*/
public class XApp extends Application {
private static final String TAG = "XApp";
public static final String XPOSED_INSTALLER_PACKAGE = "de.robv.android.xposed.installer";
private static XApp gApp;
public static XApp getApp() {
return gApp;
}
@Override
protected void attachBaseContext(Context base) {
gApp = this;
super.attachBaseContext(base);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
NativeEngine.disableJit(Build.VERSION.SDK_INT);
}
VASettings.ENABLE_IO_REDIRECT = true;
VASettings.ENABLE_INNER_SHORTCUT = false;
try {
VirtualCore.get().startup(base);
} catch (Throwable e) {
e.printStackTrace();
}
}
@Override
public void onCreate() {
super.onCreate();
VirtualCore virtualCore = VirtualCore.get();
virtualCore.initialize(new MyVirtualInitializer(this, virtualCore));
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/abs/BasePresenter.java
================================================
package io.virtualapp.abs;
/**
* @author Lody
*/
public interface BasePresenter {
void start();
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/abs/BaseView.java
================================================
package io.virtualapp.abs;
import android.app.Activity;
import android.content.Context;
/**
* @author Lody
*/
public interface BaseView {
Activity getActivity();
Context getContext();
void setPresenter(T presenter);
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/abs/Callback.java
================================================
package io.virtualapp.abs;
/**
* @author Lody
*/
public interface Callback {
void callback(T result);
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/abs/ui/VActivity.java
================================================
package io.virtualapp.abs.ui;
import android.app.Activity;
import android.content.Context;
import android.support.annotation.IdRes;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import org.jdeferred.android.AndroidDeferredManager;
import io.virtualapp.abs.BaseView;
/**
* @author Lody
*/
public class VActivity extends AppCompatActivity {
/**
* Implement of {@link BaseView#getActivity()}
*/
public Activity getActivity() {
return this;
}
/**
* Implement of {@link BaseView#getContext()} ()}
*/
public Context getContext() {
return this;
}
protected AndroidDeferredManager defer() {
return VUiKit.defer();
}
public Fragment findFragmentById(@IdRes int id) {
return getSupportFragmentManager().findFragmentById(id);
}
public void replaceFragment(@IdRes int id, Fragment fragment) {
getSupportFragmentManager().beginTransaction().replace(id, fragment).commit();
}
@Override
protected void onStart() {
super.onStart();
}
@Override
protected void onStop() {
super.onStop();
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/abs/ui/VFragment.java
================================================
package io.virtualapp.abs.ui;
import org.jdeferred.android.AndroidDeferredManager;
import android.app.Activity;
import android.support.v4.app.Fragment;
import io.virtualapp.abs.BasePresenter;
/**
* @author Lody
*/
public class VFragment extends Fragment {
protected T mPresenter;
public T getPresenter() {
return mPresenter;
}
public void setPresenter(T presenter) {
this.mPresenter = presenter;
}
protected AndroidDeferredManager defer() {
return VUiKit.defer();
}
public void finishActivity() {
Activity activity = getActivity();
if (activity != null) {
activity.finish();
}
}
public void destroy() {
finishActivity();
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/abs/ui/VUiKit.java
================================================
package io.virtualapp.abs.ui;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.TypedValue;
import org.jdeferred.android.AndroidDeferredManager;
/**
* @author Lody
*
* A set of tools for UI.
*/
public class VUiKit {
private static final AndroidDeferredManager gDM = new AndroidDeferredManager();
private static final Handler gUiHandler = new Handler(Looper.getMainLooper());
public static AndroidDeferredManager defer() {
return gDM;
}
public static int dpToPx(Context context, int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
context.getResources().getDisplayMetrics());
}
public static void post(Runnable r) {
gUiHandler.post(r);
}
public static void postDelayed(long delay, Runnable r) {
gUiHandler.postDelayed(r, delay);
}
public static void sleep(long time) {
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/delegate/BaseCrashHandler.java
================================================
package io.virtualapp.delegate;
import android.annotation.SuppressLint;
import android.os.Looper;
import android.util.Log;
import com.lody.virtual.client.core.CrashHandler;
/**
* author: weishu on 18/3/10.
*/
public class BaseCrashHandler implements CrashHandler {
protected static final String TAG = "XApp";
@SuppressLint("ApplySharedPref")
@Override
public void handleUncaughtException(Thread t, Throwable e) {
if (t == Looper.getMainLooper().getThread()) {
System.exit(0);
} else {
Log.e(TAG, "ignore uncaught exception of sub thread: " + t);
}
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/delegate/BaseVirtualInitializer.java
================================================
package io.virtualapp.delegate;
import android.app.Application;
import com.lody.virtual.client.core.VirtualCore;
import com.lody.virtual.os.VEnvironment;
import jonathanfinerty.once.Once;
import me.weishu.exposed.LogcatService;
import static io.virtualapp.XApp.XPOSED_INSTALLER_PACKAGE;
/**
* @author weishu
* @date 2019/2/25.
*/
public class BaseVirtualInitializer extends VirtualCore.VirtualInitializer {
protected Application application;
protected VirtualCore virtualCore;
public BaseVirtualInitializer(Application application, VirtualCore core) {
this.application = application;
this.virtualCore = core;
}
@Override
public void onMainProcess() {
Once.initialise(application);
}
@Override
public void onVirtualProcess() {
virtualCore.setCrashHandler(new BaseCrashHandler());
//listener components
virtualCore.setComponentDelegate(new MyComponentDelegate());
//fake phone imei,macAddress,BluetoothAddress
virtualCore.setPhoneInfoDelegate(new MyPhoneInfoDelegate());
//fake task description's icon and title
virtualCore.setTaskDescriptionDelegate(new MyTaskDescDelegate());
// ensure the logcat service alive when every virtual process start.
LogcatService.start(application, VEnvironment.getDataUserPackageDirectory(0, XPOSED_INSTALLER_PACKAGE));
}
@Override
public void onServerProcess() {
virtualCore.setAppRequestListener(new MyAppRequestListener(application));
virtualCore.addVisibleOutsidePackage("com.tencent.mobileqq");
virtualCore.addVisibleOutsidePackage("com.tencent.mobileqqi");
virtualCore.addVisibleOutsidePackage("com.tencent.minihd.qq");
virtualCore.addVisibleOutsidePackage("com.tencent.qqlite");
virtualCore.addVisibleOutsidePackage("com.facebook.katana");
virtualCore.addVisibleOutsidePackage("com.whatsapp");
virtualCore.addVisibleOutsidePackage("com.tencent.mm");
virtualCore.addVisibleOutsidePackage("com.immomo.momo");
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/delegate/MyAppRequestListener.java
================================================
package io.virtualapp.delegate;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.widget.Toast;
import com.lody.virtual.client.core.VirtualCore;
import java.io.File;
import io.virtualapp.sys.InstallerActivity;
/**
* @author Lody
*/
public class MyAppRequestListener implements VirtualCore.AppRequestListener {
private final Context context;
public MyAppRequestListener(Context context) {
this.context = context;
}
@Override
public void onRequestInstall(String path) {
try {
Intent t = new Intent(context, InstallerActivity.class);
t.setDataAndType(Uri.fromFile(new File(path)), "application/vnd.android.package-archive");
t.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(t);
} catch (Throwable e) {
e.printStackTrace();
}
}
@Override
public void onRequestUninstall(String pkg) {
Toast.makeText(context, "Uninstall: " + pkg, Toast.LENGTH_SHORT).show();
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/delegate/MyComponentDelegate.java
================================================
package io.virtualapp.delegate;
import android.app.Activity;
import android.app.Application;
import android.content.Intent;
import com.lody.virtual.client.hook.delegate.ComponentDelegate;
import com.lody.virtual.helper.utils.Reflect;
import java.io.File;
public class MyComponentDelegate implements ComponentDelegate {
@Override
public void beforeApplicationCreate(Application application) {
}
@Override
public void afterApplicationCreate(Application application) {
}
@Override
public void beforeActivityCreate(Activity activity) {
}
@Override
public void beforeActivityResume(Activity activity) {
}
@Override
public void beforeActivityPause(Activity activity) {
}
@Override
public void beforeActivityDestroy(Activity activity) {
}
@Override
public void afterActivityCreate(Activity activity) {
}
@Override
public void afterActivityResume(Activity activity) {
}
@Override
public void afterActivityPause(Activity activity) {
}
@Override
public void afterActivityDestroy(Activity activity) {
}
@Override
public void onSendBroadcast(Intent intent) {
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/delegate/MyPhoneInfoDelegate.java
================================================
package io.virtualapp.delegate;
import com.lody.virtual.client.hook.delegate.PhoneInfoDelegate;
/**
* Fake the Device ID.
*/
public class MyPhoneInfoDelegate implements PhoneInfoDelegate {
@Override
public String getDeviceId(String oldDeviceId, int userId) {
return oldDeviceId;
}
@Override
public String getBluetoothAddress(String oldAddress, int userId) {
return oldAddress;
}
@Override
public String getMacAddress(String oldAddress, int userId) {
return oldAddress;
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/delegate/MyTaskDescDelegate.java
================================================
package io.virtualapp.delegate;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.os.Build;
import com.lody.virtual.client.hook.delegate.TaskDescriptionDelegate;
import com.lody.virtual.os.VUserManager;
/**
* Patch the task description with the (Virtual) user name
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class MyTaskDescDelegate implements TaskDescriptionDelegate {
@Override
public ActivityManager.TaskDescription getTaskDescription(ActivityManager.TaskDescription oldTaskDescription) {
if (oldTaskDescription == null) {
return null;
}
String labelPrefix = "[" + VUserManager.get().getUserName() + "] ";
String oldLabel = oldTaskDescription.getLabel() != null ? oldTaskDescription.getLabel() : "";
if (!oldLabel.startsWith(labelPrefix)) {
// Is it really necessary?
return new ActivityManager.TaskDescription(labelPrefix + oldTaskDescription.getLabel(), oldTaskDescription.getIcon(), oldTaskDescription.getPrimaryColor());
} else {
return oldTaskDescription;
}
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/dev/CmdReceiver.java
================================================
package io.virtualapp.dev;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.text.TextUtils;
import android.widget.Toast;
import com.lody.virtual.client.core.InstallStrategy;
import com.lody.virtual.client.core.VirtualCore;
import com.lody.virtual.remote.InstallResult;
import io.virtualapp.BuildConfig;
import io.virtualapp.home.LoadingActivity;
/**
* author: weishu on 18/2/23.
*/
public class CmdReceiver extends BroadcastReceiver {
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";
private static final String CMD_UPDATE = "update";
private static final String CMD_REBOOT = "reboot";
private static final String CMD_LAUNCH = "launch";
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (!ACTION.equalsIgnoreCase(action)) {
return;
}
String cmd = intent.getStringExtra(KEY_CMD);
if (TextUtils.isEmpty(cmd)) {
showTips(context, "No cmd found!");
return;
}
if (CMD_REBOOT.equalsIgnoreCase(cmd)) {
VirtualCore.get().killAllApps();
showTips(context, "Reboot Success!!");
return;
}
if (CMD_UPDATE.equalsIgnoreCase(cmd)) {
String pkg = intent.getStringExtra(KEY_PKG);
if (TextUtils.isEmpty(pkg)) {
showTips(context, "Please tell me the update package!!");
return;
}
PackageManager packageManager = context.getPackageManager();
if (packageManager == null) {
showTips(context, "system error, update failed!");
return;
}
try {
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(pkg, 0);
String apkPath = applicationInfo.sourceDir;
InstallResult installResult = VirtualCore.get().installPackage(apkPath, InstallStrategy.UPDATE_IF_EXIST);
if (installResult.isSuccess) {
if (installResult.isUpdate) {
showTips(context, "Update " + pkg + " Success!!");
}
} else {
showTips(context, "Update " + pkg + " failed: " + installResult.error);
}
} catch (PackageManager.NameNotFoundException e) {
showTips(context, "Can not found " + pkg + " outside!");
}
} else if (CMD_LAUNCH.equalsIgnoreCase(cmd)) {
String pkg = intent.getStringExtra(KEY_PKG);
if (TextUtils.isEmpty(pkg)) {
showTips(context, "Please tell me the launch package!!");
return;
}
String uid = intent.getStringExtra(KEY_UID);
int userId = 0;
if (!TextUtils.isEmpty(uid)){
try {
userId = Integer.parseInt(uid);
}catch (NumberFormatException e){
e.printStackTrace();
}
}
LoadingActivity.launch(context, pkg, userId);
}
}
private void showTips(Context context, String tips) {
Toast.makeText(context, tips, Toast.LENGTH_SHORT).show();
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/glide/GlideUtils.java
================================================
package io.virtualapp.glide;
import android.content.Context;
import android.support.annotation.DrawableRes;
import android.widget.ImageView;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import static io.virtualapp.glide.PackageIconResourceLoader.DATA_PACKAGE_FILE_PATH_PREFIX;
import static io.virtualapp.glide.PackageIconResourceLoader.DATA_PACKAGE_PREFIX;
/**
* Created by Windy on 2018/10/25
*/
public class GlideUtils {
public static void loadInstalledPackageIcon(Context context, String packageName, ImageView target, @DrawableRes int placeHolder) {
GlideApp.with(context)
.load(DATA_PACKAGE_PREFIX + packageName)
.placeholder(placeHolder)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(target);
}
public static void loadPackageIconFromApkFile(Context context, String apkFilePath, ImageView target, @DrawableRes int placeHolder) {
GlideApp.with(context)
.load(DATA_PACKAGE_FILE_PATH_PREFIX + apkFilePath)
.placeholder(placeHolder)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(target);
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/glide/MyGlideModule.java
================================================
package io.virtualapp.glide;
import android.content.Context;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.bumptech.glide.Registry;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.load.engine.cache.LruResourceCache;
import com.bumptech.glide.load.engine.cache.MemorySizeCalculator;
import com.bumptech.glide.module.AppGlideModule;
import com.lody.virtual.helper.utils.VLog;
import java.io.InputStream;
/**
* Created by Windy on 2018/10/25
*/
@GlideModule
public class MyGlideModule extends AppGlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context)
.build();
builder.setMemoryCache(new LruResourceCache(calculator.getMemoryCacheSize() / 2));
VLog.i("MyGlideModule", "applyOptions");
}
@Override
public boolean isManifestParsingEnabled() {
return false;
}
@Override
public void registerComponents(Context context, Glide glide, Registry registry) {
super.registerComponents(context, glide, registry);
registry.prepend(String.class, InputStream.class, new PackageIconResourceLoaderFactory(context));
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/glide/PackageIconResourceDataFetcher.java
================================================
package io.virtualapp.glide;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.data.DataFetcher;
import com.lody.virtual.helper.utils.VLog;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import static io.virtualapp.glide.PackageIconResourceLoader.DATA_PACKAGE_FILE_PATH_PREFIX;
import static io.virtualapp.glide.PackageIconResourceLoader.DATA_PACKAGE_PREFIX;
/**
* Created by Windy on 2018/10/25
*/
public class PackageIconResourceDataFetcher implements DataFetcher {
private static final String TAG = PackageIconResourceDataFetcher.class.getSimpleName();
private Context context;
private String packageModel;
private InputStream data;
public PackageIconResourceDataFetcher(Context context, String packageName) {
this.context = context.getApplicationContext();
this.packageModel = packageName;
}
@Override
public void loadData(@NonNull Priority priority, @NonNull DataCallback super InputStream> callback) {
try {
data = loadResource();
} catch (Exception e) {
VLog.e(TAG, "Failed to load data from asset manager", e);
callback.onLoadFailed(e);
return;
}
callback.onDataReady(data);
}
@Override
public void cleanup() {
if (data == null) {
return;
}
try {
data.close();
} catch (IOException e) {
// Ignored.
}
}
@Override
public void cancel() {
}
@NonNull
@Override
public Class getDataClass() {
return InputStream.class;
}
@NonNull
@Override
public DataSource getDataSource() {
return DataSource.LOCAL;
}
//load icon res accord to package name, or apk path
private InputStream loadResource() {
PackageInfo packageInfo = null;
Drawable drawable = null;
try {
packageInfo = getPackageInfo();
if (packageInfo == null) {
return null;
}
drawable = packageInfo.applicationInfo.loadIcon(context.getPackageManager());
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
if (drawable == null) {
return null;
}
return drawableToInputStream(drawable);
}
private PackageInfo getPackageInfo() throws PackageManager.NameNotFoundException {
if (packageModel.startsWith(DATA_PACKAGE_PREFIX)) {
return context.getPackageManager().getPackageInfo(getPackageTrueModel(DATA_PACKAGE_PREFIX), 0);
} else if (packageModel.startsWith(DATA_PACKAGE_FILE_PATH_PREFIX)) {
return context.getPackageManager().getPackageArchiveInfo(getPackageTrueModel(DATA_PACKAGE_FILE_PATH_PREFIX), 0);
}
return null;
}
private String getPackageTrueModel(String prefix) {
return packageModel.replaceAll(prefix, "");
}
private InputStream drawableToInputStream(Drawable drawable) {
Bitmap bitmap = drawableToBitmap(drawable);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); //use the compression format of your need
return new ByteArrayInputStream(stream.toByteArray());
}
private static Bitmap drawableToBitmap(Drawable drawable) {
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
int width = drawable.getIntrinsicWidth();
width = width > 0 ? width : 1;
int height = drawable.getIntrinsicHeight();
height = height > 0 ? height : 1;
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/glide/PackageIconResourceLoader.java
================================================
package io.virtualapp.glide;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.signature.ObjectKey;
import java.io.InputStream;
/**
* Created by Windy on 2018/10/25
*/
public class PackageIconResourceLoader implements ModelLoader {
public static final String DATA_PACKAGE_PREFIX = "data:packageName/";
public static final String DATA_PACKAGE_FILE_PATH_PREFIX = "data:packageFilePath/";
private Context context;
public PackageIconResourceLoader(Context context) {
this.context = context;
}
@Nullable
@Override
public LoadData buildLoadData(@NonNull String model, int width, int height, @NonNull Options options) {
return new LoadData<>(new ObjectKey(model), new PackageIconResourceDataFetcher(context, model));
}
@Override
public boolean handles(@NonNull String model) {
return model.startsWith(DATA_PACKAGE_PREFIX) || model.startsWith(DATA_PACKAGE_FILE_PATH_PREFIX);
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/glide/PackageIconResourceLoaderFactory.java
================================================
package io.virtualapp.glide;
import android.content.Context;
import android.support.annotation.NonNull;
import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.load.model.ModelLoaderFactory;
import com.bumptech.glide.load.model.MultiModelLoaderFactory;
import java.io.InputStream;
/**
* Created by Windy on 2018/10/25
*/
public class PackageIconResourceLoaderFactory implements ModelLoaderFactory {
private Context context;
public PackageIconResourceLoaderFactory(Context context) {
this.context = context;
}
@NonNull
@Override
public ModelLoader build(@NonNull MultiModelLoaderFactory multiFactory) {
return new PackageIconResourceLoader(context);
}
@Override
public void teardown() {
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/gms/FakeGms.java
================================================
package io.virtualapp.gms;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.util.Log;
import com.lody.virtual.client.core.InstallStrategy;
import com.lody.virtual.client.core.VirtualCore;
import com.lody.virtual.os.VEnvironment;
import com.lody.virtual.remote.InstallResult;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import io.virtualapp.R;
import io.virtualapp.abs.ui.VUiKit;
import io.virtualapp.utils.DialogUtil;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
/**
* @author weishu
* @date 2018/6/9.
*/
public class FakeGms {
private static final String TAG = "FakeGms";
private static final String GMS_CONFIG_URL = "http://vaexposed.weishu.me/gms.json";
private static final String GMS_PKG = "com.google.android.gms";
private static final String GSF_PKG = "com.google.android.gsf";
private static final String STORE_PKG = "com.android.vending";
private static final String FAKE_GAPPS_PKG = "com.thermatk.android.xf.fakegapps";
private static ExecutorService executorService = Executors.newSingleThreadExecutor();
public static void uninstallGms(Activity activity) {
if (activity == null) {
return;
}
AlertDialog failDialog = new AlertDialog.Builder(activity, R.style.Theme_AppCompat_DayNight_Dialog_Alert)
.setTitle(R.string.uninstall_gms_title)
.setMessage(R.string.uninstall_gms_content)
.setPositiveButton(R.string.uninstall_gms_ok, ((dialog1, which1) -> {
ProgressDialog dialog = new ProgressDialog(activity);
dialog.show();
VUiKit.defer().when(() -> {
VirtualCore.get().uninstallPackage(GMS_PKG);
VirtualCore.get().uninstallPackage(GSF_PKG);
VirtualCore.get().uninstallPackage(STORE_PKG);
VirtualCore.get().uninstallPackage(FAKE_GAPPS_PKG);
}).then((v) -> {
dialog.dismiss();
AlertDialog hits = new AlertDialog.Builder(activity, R.style.Theme_AppCompat_DayNight_Dialog_Alert)
.setTitle(R.string.uninstall_gms_title)
.setMessage(R.string.uninstall_gms_success)
.setPositiveButton(android.R.string.ok, null)
.create();
DialogUtil.showDialog(hits);
}).fail((v) -> {
dialog.dismiss();
});
}))
.setNegativeButton(android.R.string.cancel, null)
.create();
DialogUtil.showDialog(failDialog);
}
public static boolean isAlreadyInstalled(Context context) {
if (context == null) {
return false;
}
boolean alreadyInstalled = true;
if (!VirtualCore.get().isAppInstalled(GMS_PKG)) {
alreadyInstalled = false;
}
if (!VirtualCore.get().isAppInstalled(GSF_PKG)) {
alreadyInstalled = false;
}
if (!VirtualCore.get().isAppInstalled(STORE_PKG)) {
alreadyInstalled = false;
}
if (!VirtualCore.get().isAppInstalled(FAKE_GAPPS_PKG)) {
alreadyInstalled = false;
}
return alreadyInstalled;
}
public static void installGms(Activity activity) {
if (activity == null) {
return;
}
AlertDialog alertDialog = new AlertDialog.Builder(activity, R.style.Theme_AppCompat_DayNight_Dialog_Alert)
.setTitle(R.string.install_gms_title)
.setMessage(R.string.install_gms_content)
.setPositiveButton(android.R.string.ok, ((dialog, which) -> {
// show a loading dialog and start install gms.
ProgressDialog progressDialog = new ProgressDialog(activity);
progressDialog.setCancelable(false);
progressDialog.show();
executorService.submit(() -> {
String failMsg = installGmsInternal(activity, progressDialog);
Log.i(TAG, "install gms result: " + failMsg);
try {
progressDialog.dismiss();
} catch (Throwable e) {
e.printStackTrace();
}
if (failMsg == null) {
activity.runOnUiThread(() -> {
AlertDialog failDialog = new AlertDialog.Builder(activity, R.style.Theme_AppCompat_DayNight_Dialog_Alert)
.setTitle(R.string.install_gms_title)
.setMessage(R.string.install_gms_success)
.setPositiveButton(android.R.string.ok, null)
.create();
DialogUtil.showDialog(failDialog);
});
} else {
activity.runOnUiThread(() -> {
AlertDialog failDialog = new AlertDialog.Builder(activity, R.style.Theme_AppCompat_DayNight_Dialog_Alert)
.setTitle(R.string.install_gms_fail_title)
.setMessage(R.string.install_gms_fail_content)
.setPositiveButton(R.string.install_gms_fail_ok, ((dialog1, which1) -> {
try {
Intent t = new Intent(Intent.ACTION_VIEW);
t.setData(Uri.parse("https://github.com/android-hacker/VirtualXposed/wiki/Google-service-support"));
activity.startActivity(t);
} catch (Throwable ignored) {
ignored.printStackTrace();
}
}))
.setNegativeButton(android.R.string.cancel, null)
.create();
DialogUtil.showDialog(failDialog);
});
}
});
}))
.setNegativeButton(android.R.string.cancel, null)
.create();
DialogUtil.showDialog(alertDialog);
}
private static String installGmsInternal(Activity activity, ProgressDialog dialog) {
File cacheDir = activity.getCacheDir();
// 下载配置文件,得到各自的URL
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build();
Request request = new Request.Builder()
.url(GMS_CONFIG_URL)
.build();
updateMessage(activity, dialog, "Fetching gms config...");
Response response;
try {
response = client.newCall(request).execute();
} catch (IOException e) {
return "Download gms config failed, please check your network, error: 0";
}
if (!response.isSuccessful()) {
return "Download gms config failed, please check your network, error: 1";
}
Log.i(TAG, "response success: " + response.code());
if (200 != response.code()) {
return "Download gms config failed, please check your network, error: 2";
}
updateMessage(activity, dialog, "Parsing gms config...");
ResponseBody body = response.body();
if (body == null) {
return "Download gms config failed, please check your network, error: 3";
}
String string = null;
try {
string = body.string();
} catch (IOException e) {
return "Download gms config failed, please check your network, error: 4";
}
JSONObject jsonObject = null;
try {
jsonObject = new JSONObject(string);
} catch (JSONException e) {
return "Download gms config failed, please check your network, error: 5";
}
String gmsCoreUrl = null;
try {
gmsCoreUrl = jsonObject.getString("gms");
} catch (JSONException e) {
return "Download gms config failed, please check your network, error: 6";
}
String gmsServiceUrl = null;
try {
gmsServiceUrl = jsonObject.getString("gsf");
} catch (JSONException e) {
return "Download gms config failed, please check your network, error: 7";
}
String storeUrl = null;
try {
storeUrl = jsonObject.getString("store");
} catch (JSONException e) {
return "Download gms config failed, please check your network, error: 8";
}
String fakeGappsUrl = null;
try {
fakeGappsUrl = jsonObject.getString("fakegapps");
} catch (JSONException e) {
return "Download gms config failed, please check your network, error: 9";
}
String yalpStoreUrl = null;
try {
yalpStoreUrl = jsonObject.getString("yalp");
} catch (JSONException e) {
// ignore.
Log.i(TAG, "Download gms config failed, please check your network");
}
updateMessage(activity, dialog, "config parse success!");
File gmsCoreFile = new File(cacheDir, "gms.apk");
File gmsServiceFile = new File(cacheDir, "gsf.apk");
File storeFile = new File(cacheDir, "store.apk");
File fakeGappsFile = new File(cacheDir, "fakegapps.apk");
File yalpStoreFile = new File(cacheDir, "yalpStore.apk");
// clear old files.
if (gmsCoreFile.exists()) {
gmsCoreFile.delete();
}
if (gmsServiceFile.exists()) {
gmsServiceFile.delete();
}
if (storeFile.exists()) {
storeFile.delete();
}
if (fakeGappsFile.exists()) {
fakeGappsFile.delete();
}
boolean downloadResult = downloadFile(gmsCoreUrl, gmsCoreFile,
(progress) -> updateMessage(activity, dialog, "download gms core..." + progress + "%"));
if (!downloadResult) {
return "Download gms config failed, please check your network, error: 10";
}
downloadResult = downloadFile(gmsServiceUrl, gmsServiceFile,
(progress -> updateMessage(activity, dialog, "download gms service framework proxy.." + progress + "%")));
if (!downloadResult) {
return "Download gms config failed, please check your network, error: 11";
}
updateMessage(activity, dialog, "download gms store...");
downloadResult = downloadFile(storeUrl, storeFile,
(progress -> updateMessage(activity, dialog, "download gms store.." + progress + "%")));
if (!downloadResult) {
return "Download gms config failed, please check your network, error: 12";
}
downloadResult = downloadFile(fakeGappsUrl, fakeGappsFile,
(progress -> updateMessage(activity, dialog, "download gms Xposed module.." + progress + "%")));
if (!downloadResult) {
return "Download gms config failed, please check your network, error: 13";
}
if (yalpStoreUrl != null) {
downloadFile(yalpStoreUrl,yalpStoreFile,
(progress -> updateMessage(activity, dialog, "download yalp store.." + progress + "%")));
}
updateMessage(activity, dialog, "installing gms core");
InstallResult installResult = VirtualCore.get().installPackage(gmsCoreFile.getAbsolutePath(), InstallStrategy.UPDATE_IF_EXIST);
if (!installResult.isSuccess) {
return "install gms core failed: " + installResult.error;
}
updateMessage(activity, dialog, "installing gms service framework...");
installResult = VirtualCore.get().installPackage(gmsServiceFile.getAbsolutePath(), InstallStrategy.UPDATE_IF_EXIST);
if (!installResult.isSuccess) {
return "install gms service framework failed: " + installResult.error;
}
updateMessage(activity, dialog, "installing gms store...");
installResult = VirtualCore.get().installPackage(storeFile.getAbsolutePath(), InstallStrategy.UPDATE_IF_EXIST);
if (!installResult.isSuccess) {
return "install gms store failed: " + installResult.error;
}
updateMessage(activity, dialog, "installing gms Xposed module...");
installResult = VirtualCore.get().installPackage(fakeGappsFile.getAbsolutePath(), InstallStrategy.UPDATE_IF_EXIST);
if (!installResult.isSuccess) {
return "install gms xposed module failed: " + installResult.error;
}
if (yalpStoreFile.exists()) {
updateMessage(activity, dialog, "installing yalp store...");
VirtualCore.get().installPackage(yalpStoreFile.getAbsolutePath(), InstallStrategy.UPDATE_IF_EXIST);
}
// Enable the Xposed module.
File dataDir = VEnvironment.getDataUserPackageDirectory(0, "de.robv.android.xposed.installer");
File modulePath = VEnvironment.getPackageResourcePath(FAKE_GAPPS_PKG);
File configDir = new File(dataDir, "exposed_conf" + File.separator + "modules.list");
FileWriter writer = null;
try {
writer = new FileWriter(configDir, true);
writer.append(modulePath.getAbsolutePath());
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// success!!!
return null;
}
private static void updateMessage(Activity activity, ProgressDialog dialog, String msg) {
if (activity == null || dialog == null || TextUtils.isEmpty(msg)) {
return;
}
Log.i(TAG, "update dialog message: " + msg);
activity.runOnUiThread(() -> {
dialog.setMessage(msg);
});
}
public interface DownloadListener {
void onProgress(int progress);
}
public static boolean downloadFile(String url, File outFile, DownloadListener listener) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
FileOutputStream fos = null;
try {
Response response = client.newCall(request).execute();
if (response.code() != 200) {
return false;
}
ResponseBody body = response.body();
if (body == null) {
return false;
}
long toal = body.contentLength();
long sum = 0;
InputStream inputStream = body.byteStream();
fos = new FileOutputStream(outFile);
byte[] buffer = new byte[1024];
int count = 0;
while ((count = inputStream.read(buffer)) >= 0) {
fos.write(buffer, 0, count);
sum += count;
int progress = (int) ((sum * 1.0) / toal * 100);
if (listener != null) {
listener.onProgress(progress);
}
}
fos.flush();
return true;
} catch (IOException e) {
return false;
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/home/ListAppActivity.java
================================================
package io.virtualapp.home;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import io.virtualapp.R;
import io.virtualapp.VCommends;
import io.virtualapp.abs.ui.VActivity;
import io.virtualapp.home.adapters.AppPagerAdapter;
/**
* @author Lody
*/
public class ListAppActivity extends VActivity {
private Toolbar mToolBar;
private TabLayout mTabLayout;
private ViewPager mViewPager;
public static void gotoListApp(Activity activity) {
Intent intent = new Intent(activity, ListAppActivity.class);
activity.startActivityForResult(intent, VCommends.REQUEST_SELECT_APP);
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_clone_app);
mToolBar = findViewById(R.id.clone_app_tool_bar);
mTabLayout = mToolBar.findViewById(R.id.clone_app_tab_layout);
mViewPager = findViewById(R.id.clone_app_view_pager);
mViewPager.setAdapter(new AppPagerAdapter(getSupportFragmentManager()));
mTabLayout.setupWithViewPager(mViewPager);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/home/ListAppContract.java
================================================
package io.virtualapp.home;
import java.util.List;
import io.virtualapp.abs.BasePresenter;
import io.virtualapp.abs.BaseView;
import io.virtualapp.home.models.AppInfo;
/**
* @author Lody
* @version 1.0
*/
/*package*/ class ListAppContract {
interface ListAppView extends BaseView {
void startLoading();
void loadFinish(List infoList);
}
interface ListAppPresenter extends BasePresenter {
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/home/ListAppFragment.java
================================================
package io.virtualapp.home;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.OrientationHelper;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.lody.virtual.helper.compat.NativeLibraryHelperCompat;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import io.virtualapp.R;
import io.virtualapp.XApp;
import io.virtualapp.abs.ui.VFragment;
import io.virtualapp.home.adapters.CloneAppListAdapter;
import io.virtualapp.home.models.AppInfo;
import io.virtualapp.home.models.AppInfoLite;
import io.virtualapp.sys.Installd;
import io.virtualapp.widgets.DragSelectRecyclerView;
/**
* @author Lody
*/
public class ListAppFragment extends VFragment implements ListAppContract.ListAppView {
private static final String KEY_SELECT_FROM = "key_select_from";
private static final int REQUEST_GET_FILE = 1;
private DragSelectRecyclerView mRecyclerView;
private ProgressBar mProgressBar;
private Button mInstallButton;
private CloneAppListAdapter mAdapter;
private View mSelectFromExternal;
public static ListAppFragment newInstance(File selectFrom) {
Bundle args = new Bundle();
if (selectFrom != null)
args.putString(KEY_SELECT_FROM, selectFrom.getPath());
ListAppFragment fragment = new ListAppFragment();
fragment.setArguments(args);
return fragment;
}
private File getSelectFrom() {
Bundle bundle = getArguments();
if (bundle != null) {
String selectFrom = bundle.getString(KEY_SELECT_FROM);
if (selectFrom != null) {
return new File(selectFrom);
}
}
return null;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_list_app, null);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mAdapter.saveInstanceState(outState);
}
private void chooseInstallWay(Runnable runnable, String path) {
AlertDialog alertDialog = new AlertDialog.Builder(getContext())
.setTitle(R.string.install_choose_way)
.setMessage(R.string.install_choose_content)
.setPositiveButton(R.string.install_choose_taichi, (dialog, which) -> {
PackageManager packageManager = getActivity().getPackageManager();
try {
packageManager.getPackageInfo("me.weishu.exp", 0);
Intent intent = new Intent();
intent.setComponent(new ComponentName("me.weishu.exp", "me.weishu.exp.ui.MainActivity"));
intent.putExtra("path", path);
startActivity(intent);
} catch (PackageManager.NameNotFoundException e) {
AlertDialog showInstallDialog = new AlertDialog.Builder(getContext())
.setTitle(android.R.string.dialog_alert_title)
.setMessage(R.string.install_taichi_not_exist)
.setPositiveButton(R.string.install_go_to_install_exp, (dialog1, which1) -> {
Intent t = new Intent(Intent.ACTION_VIEW);
t.setData(Uri.parse("https://www.coolapk.com/apk/me.weishu.exp"));
startActivity(t);
})
.create();
showInstallDialog.show();
} catch (Throwable e) {
AlertDialog showInstallDialog = new AlertDialog.Builder(getContext())
.setTitle(android.R.string.dialog_alert_title)
.setMessage(R.string.install_taichi_while_old_version)
.setPositiveButton(R.string.install_go_latest_exp, (dialog1, which1) -> {
Intent t = new Intent(Intent.ACTION_VIEW);
t.setData(Uri.parse("https://taichi.cool"));
startActivity(t);
})
.create();
showInstallDialog.show();
}
finishActivity();
}).setNegativeButton("VirtualXposed", (dialog, which) -> {
if (runnable != null) {
runnable.run();
}
finishActivity();
}).setNeutralButton(R.string.what_is_exp, ((dialog, which) -> {
Intent t = new Intent(Intent.ACTION_VIEW);
t.setData(Uri.parse("https://taichi.cool"));
startActivity(t);
}))
.create();
try {
alertDialog.show();
} catch (Throwable ignored) {
}
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
mRecyclerView = (DragSelectRecyclerView) view.findViewById(R.id.select_app_recycler_view);
mProgressBar = (ProgressBar) view.findViewById(R.id.select_app_progress_bar);
mInstallButton = (Button) view.findViewById(R.id.select_app_install_btn);
mSelectFromExternal = view.findViewById(R.id.select_app_from_external);
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(1, OrientationHelper.VERTICAL));
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL);
dividerItemDecoration.setDrawable(new ColorDrawable(0x1f000000));
mRecyclerView.addItemDecoration(dividerItemDecoration);
mAdapter = new CloneAppListAdapter(getActivity(), getSelectFrom());
mRecyclerView.setAdapter(mAdapter);
mAdapter.setOnItemClickListener(new CloneAppListAdapter.ItemEventListener() {
@Override
public void onItemClick(AppInfo info, int position) {
if (!NativeLibraryHelperCompat.isApk64(info.path)) {
Toast.makeText(getContext(), R.string.unsupported_for_32bit_app, Toast.LENGTH_SHORT).show();
return;
}
int count = mAdapter.getSelectedCount();
if (!mAdapter.isIndexSelected(position)) {
if (count >= 9) {
Toast.makeText(getContext(), R.string.install_too_much_once_time, Toast.LENGTH_SHORT).show();
return;
}
}
mAdapter.toggleSelected(position);
}
@Override
public boolean isSelectable(int position) {
return mAdapter.isIndexSelected(position) || mAdapter.getSelectedCount() < 9;
}
});
mAdapter.setSelectionListener(count -> {
mInstallButton.setEnabled(count > 0);
mInstallButton.setText(String.format(Locale.ENGLISH, XApp.getApp().getResources().getString(R.string.install_d), count));
});
mInstallButton.setOnClickListener(v -> {
Integer[] selectedIndices = mAdapter.getSelectedIndices();
ArrayList dataList = new ArrayList(selectedIndices.length);
for (int index : selectedIndices) {
AppInfo info = mAdapter.getItem(index);
dataList.add(new AppInfoLite(info.packageName, info.path, info.fastOpen, info.disableMultiVersion));
}
if (dataList.size() > 0) {
String path = dataList.get(0).path;
chooseInstallWay(() -> {
Activity activity = getActivity();
if (activity == null) {
return;
}
Installd.startInstallerActivity(activity, dataList);
activity.setResult(Activity.RESULT_OK);
}, path);
}
});
mSelectFromExternal.setOnClickListener(v -> {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("application/vnd.android.package-archive"); // apk file
intent.addCategory(Intent.CATEGORY_OPENABLE);
try {
startActivityForResult(intent, REQUEST_GET_FILE);
} catch (Throwable ignored) {
Toast.makeText(getActivity(), "Error", Toast.LENGTH_SHORT).show();
}
});
new ListAppPresenterImpl(getActivity(), this, getSelectFrom()).start();
}
@Override
public void startLoading() {
mProgressBar.setVisibility(View.VISIBLE);
mRecyclerView.setVisibility(View.GONE);
}
@Override
public void loadFinish(List infoList) {
mAdapter.setList(infoList);
mRecyclerView.setDragSelectActive(false, 0);
mAdapter.setSelected(0, false);
mProgressBar.setVisibility(View.GONE);
mRecyclerView.setVisibility(View.VISIBLE);
}
@Override
public void setPresenter(ListAppContract.ListAppPresenter presenter) {
this.mPresenter = presenter;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (!(requestCode == REQUEST_GET_FILE && resultCode == Activity.RESULT_OK)) {
return;
}
Activity current = getActivity();
if (current == null) {
return;
}
Uri uri = data.getData();
String path = getPath(getActivity(), uri);
if (path == null) {
return;
}
chooseInstallWay(() -> {
Installd.handleRequestFromFile(getActivity(), path);
getActivity().setResult(Activity.RESULT_OK);
}, path);
}
public static String getPath(Context context, Uri uri) {
if (uri == null) {
return null;
}
if ("content".equalsIgnoreCase(uri.getScheme())) {
String[] projection = {"_data"};
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(uri, projection, null, null, null);
int column_index = cursor.getColumnIndexOrThrow("_data");
if (cursor.moveToFirst()) {
return cursor.getString(column_index);
}
} catch (Exception e) {
// Eat it Or Log it.
} finally {
if (cursor != null) {
cursor.close();
}
}
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/home/ListAppPresenterImpl.java
================================================
package io.virtualapp.home;
import android.app.Activity;
import java.io.File;
import io.virtualapp.home.repo.AppDataSource;
import io.virtualapp.home.repo.AppRepository;
/**
* @author Lody
*/
class ListAppPresenterImpl implements ListAppContract.ListAppPresenter {
private Activity mActivity;
private ListAppContract.ListAppView mView;
private AppDataSource mRepository;
private File from;
ListAppPresenterImpl(Activity activity, ListAppContract.ListAppView view, File fromWhere) {
mActivity = activity;
mView = view;
mRepository = new AppRepository(activity);
mView.setPresenter(this);
this.from = fromWhere;
}
@Override
public void start() {
mView.setPresenter(this);
mView.startLoading();
if (from == null) {
mRepository.getInstalledApps(mActivity).done(mView::loadFinish);
}
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/home/LoadingActivity.java
================================================
package io.virtualapp.home;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.util.Log;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.lody.virtual.client.core.VirtualCore;
import com.lody.virtual.client.env.Constants;
import com.lody.virtual.client.ipc.VActivityManager;
import com.lody.virtual.client.ipc.VPackageManager;
import com.lody.virtual.helper.utils.VLog;
import com.lody.virtual.server.pm.parser.VPackage;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import io.virtualapp.R;
import io.virtualapp.abs.ui.VActivity;
import io.virtualapp.abs.ui.VUiKit;
import io.virtualapp.home.models.PackageAppData;
import io.virtualapp.home.repo.PackageAppDataStorage;
import io.virtualapp.widgets.EatBeansView;
import jonathanfinerty.once.Once;
/**
* @author Lody
*/
public class LoadingActivity extends VActivity {
private static final String TAG = "LoadingActivity";
private PackageAppData appModel;
private EatBeansView loadingView;
private static final int REQUEST_PERMISSION_CODE = 100;
private Intent intentToLaunch;
private int userToLaunch;
private long start;
public static boolean launch(Context context, String packageName, int userId) {
Intent intent = VirtualCore.get().getLaunchIntent(packageName, userId);
if (intent != null) {
Intent loadingPageIntent = new Intent(context, LoadingActivity.class);
loadingPageIntent.putExtra(Constants.PASS_PKG_NAME_ARGUMENT, packageName);
loadingPageIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
loadingPageIntent.putExtra(Constants.PASS_KEY_INTENT, intent);
loadingPageIntent.putExtra(Constants.PASS_KEY_USER, userId);
context.startActivity(loadingPageIntent);
return true;
} else {
return false;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
start = SystemClock.elapsedRealtime();
setContentView(R.layout.activity_loading);
loadingView = (EatBeansView) findViewById(R.id.loading_anim);
int userId = getIntent().getIntExtra(Constants.PASS_KEY_USER, -1);
String pkg = getIntent().getStringExtra(Constants.PASS_PKG_NAME_ARGUMENT);
appModel = PackageAppDataStorage.get().acquire(pkg);
if (appModel == null) {
Toast.makeText(getApplicationContext(), "Open App:" + pkg + " failed.", Toast.LENGTH_SHORT).show();
finish();
return;
}
ImageView iconView = (ImageView) findViewById(R.id.app_icon);
iconView.setImageDrawable(appModel.icon);
TextView nameView = (TextView) findViewById(R.id.app_name);
nameView.setText(String.format(Locale.ENGLISH, "Opening %s...", appModel.name));
Intent intent = getIntent().getParcelableExtra(Constants.PASS_KEY_INTENT);
if (intent == null) {
finish();
return;
}
VirtualCore.get().setUiCallback(intent, mUiCallback);
try {
// 如果已经在运行了,那么直接拉起,不做任何检测。
boolean uiRunning = false;
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
if (am != null) {
List runningAppProcesses = am.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo runningAppProcess : runningAppProcesses) {
String appProcessName = VActivityManager.get().getAppProcessName(runningAppProcess.pid);
if (TextUtils.equals(appProcessName, pkg)) {
uiRunning = true;
break;
}
}
}
VLog.i(TAG, pkg + "is running: " + uiRunning);
if (uiRunning) {
launchActivity(intent, userId);
return;
}
} catch (Throwable ignored) {
ignored.printStackTrace();
}
checkAndLaunch(intent, userId);
}
private void checkAndLaunch(Intent intent, int userId) {
final int RUNTIME_PERMISSION_API_LEVEL = android.os.Build.VERSION_CODES.M;
if (android.os.Build.VERSION.SDK_INT < RUNTIME_PERMISSION_API_LEVEL) {
// the device is below Android M, the permissions are granted when install, start directly
Log.i(TAG, "device's api level below Android M, do not need runtime permission.");
launchActivityWithDelay(intent, userId);
return;
}
// The device is above android M, support runtime permission.
String packageName = appModel.packageName;
String name = appModel.name;
// analyze permission
try {
ApplicationInfo applicationInfo = VPackageManager.get().getApplicationInfo(packageName, 0, 0);
int targetSdkVersion = applicationInfo.targetSdkVersion;
Log.i(TAG, "target package: " + packageName + " targetSdkVersion: " + targetSdkVersion);
if (targetSdkVersion >= RUNTIME_PERMISSION_API_LEVEL) {
Log.i(TAG, "target package support runtime permission, launch directly.");
launchActivityWithDelay(intent, userId);
} else {
intentToLaunch = intent;
userToLaunch = userId;
PackageInfo packageInfo = VPackageManager.get().getPackageInfo(packageName, PackageManager.GET_PERMISSIONS, 0);
String[] requestedPermissions = packageInfo.requestedPermissions;
Set dangerousPermissions = new HashSet<>();
for (String requestedPermission : requestedPermissions) {
if (VPackage.PermissionComponent.DANGEROUS_PERMISSION.contains(requestedPermission)) {
// dangerous permission, check it
if (ContextCompat.checkSelfPermission(this, requestedPermission) != PackageManager.PERMISSION_GRANTED) {
dangerousPermissions.add(requestedPermission);
} else {
Log.i(TAG, "permission: " + requestedPermission + " is granted, ignore.");
}
}
}
if (dangerousPermissions.isEmpty()) {
Log.i(TAG, "all permission are granted, launch directly.");
// all permission are granted, launch directly.
launchActivityWithDelay(intent, userId);
} else {
// tell user that this app need that permission
Log.i(TAG, "request permission: " + dangerousPermissions);
AlertDialog alertDialog = new AlertDialog.Builder(this, R.style.Theme_AppCompat_DayNight_Dialog_Alert)
.setTitle(R.string.permission_tip_title)
.setMessage(getResources().getString(R.string.permission_tips_content, name))
.setPositiveButton(R.string.permission_tips_confirm, (dialog, which) -> {
String[] permissionToRequest = dangerousPermissions.toArray(new String[dangerousPermissions.size()]);
try {
ActivityCompat.requestPermissions(this, permissionToRequest, REQUEST_PERMISSION_CODE);
} catch (Throwable ignored) {
}
})
.create();
try {
alertDialog.show();
} catch (Throwable ignored) {
// BadTokenException.
finish();
Toast.makeText(this, getResources().getString(R.string.start_app_failed, appModel.name), Toast.LENGTH_SHORT).show();
}
}
}
} catch (Throwable e) {
Log.e(TAG, "check permission failed: ", e);
launchActivityWithDelay(intent, userId);
}
}
private void launchActivityWithDelay(Intent intent, int userId) {
final int MAX_WAIT = 1000;
long delta = SystemClock.elapsedRealtime() - start;
long waitTime = MAX_WAIT - delta;
if (waitTime <= 0) {
launchActivity(intent, userId);
} else {
loadingView.postDelayed(() -> launchActivity(intent, userId), waitTime);
}
}
private void launchActivity(Intent intent, int userId) {
try {
VActivityManager.get().startActivity(intent, userId);
} catch (Throwable e) {
VLog.e(TAG, "start activity failed:", e);
Toast.makeText(getApplicationContext(), getResources().getString(R.string.start_app_failed, appModel.name), Toast.LENGTH_SHORT).show();
finish();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_PERMISSION_CODE) {
boolean allGranted = true;
for (int grantResult : grantResults) {
if (grantResult == PackageManager.PERMISSION_DENIED) {
allGranted = false;
break;
}
}
if (allGranted) {
if (intentToLaunch == null) {
Toast.makeText(this, getResources().getString(R.string.start_app_failed, appModel.name), Toast.LENGTH_SHORT).show();
finish();
} else {
launchActivityWithDelay(intentToLaunch, userToLaunch);
}
} else {
// 提示用户,targetSdkVersion < 23 无法使用运行时权限
Log.i(TAG, "can not use runtime permission, you must grant all permission, otherwise the app may not work!");
final String tag = "permission_tips_" + appModel.packageName.replaceAll("\\.", "_");
// TODO find a device figuring out why some permissions are not detected.
if (!Once.beenDone(tag)) {
AlertDialog alertDialog = new AlertDialog.Builder(this, R.style.Theme_AppCompat_DayNight_Dialog_Alert)
.setTitle(android.R.string.dialog_alert_title)
.setMessage(getResources().getString(R.string.permission_denied_tips_content, appModel.name))
.setPositiveButton(R.string.permission_tips_confirm, (dialog, which) -> {
finish();
Once.markDone(tag);
launchActivityWithDelay(intentToLaunch, userToLaunch);
})
.create();
try {
alertDialog.show();
} catch (Throwable ignored) {
// BadTokenException.
Toast.makeText(this, getResources().getString(R.string.start_app_failed, appModel.name), Toast.LENGTH_SHORT).show();
}
} else {
launchActivityWithDelay(intentToLaunch, userToLaunch);
finish();
}
}
}
}
private final VirtualCore.UiCallback mUiCallback = new VirtualCore.UiCallback() {
@Override
public void onAppOpened(String packageName, int userId) throws RemoteException {
finish();
}
@Override
public void onOpenFailed(String packageName, int userId) throws RemoteException {
VUiKit.defer().when(() -> {
}).done((v) -> {
if (!isFinishing()) {
Toast.makeText(getApplicationContext(), getResources().getString(R.string.start_app_failed, packageName),
Toast.LENGTH_SHORT).show();
}
});
finish();
}
};
@Override
protected void onResume() {
super.onResume();
startAnim();
}
private void startAnim() {
if (loadingView != null) {
loadingView.startAnim();
}
}
@Override
protected void onStop() {
super.onStop();
if (loadingView != null) {
loadingView.stopAnim();
}
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/home/NewHomeActivity.java
================================================
package io.virtualapp.home;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Toast;
import com.android.launcher3.LauncherFiles;
import com.google.android.apps.nexuslauncher.NexusLauncherActivity;
import com.lody.virtual.client.core.InstallStrategy;
import com.lody.virtual.client.core.VirtualCore;
import com.lody.virtual.helper.utils.DeviceUtil;
import com.lody.virtual.helper.utils.FileUtils;
import com.lody.virtual.helper.utils.MD5Utils;
import com.lody.virtual.helper.utils.VLog;
import java.io.File;
import java.io.FileOutputStream;
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;
import io.virtualapp.settings.SettingsActivity;
import io.virtualapp.update.VAVersionService;
import io.virtualapp.utils.Misc;
import jonathanfinerty.once.Once;
import static io.virtualapp.XApp.XPOSED_INSTALLER_PACKAGE;
/**
* @author weishu
* @date 18/2/9.
*/
public class NewHomeActivity extends NexusLauncherActivity {
private static final String SHOW_DOZE_ALERT_KEY = "SHOW_DOZE_ALERT_KEY";
private static final String WALLPAPER_FILE_NAME = "wallpaper.png";
private Handler mUiHandler;
private boolean mDirectlyBack = false;
private final AtomicBoolean checkXposedInstaller = new AtomicBoolean(true);
public static void goHome(Context context) {
Intent intent = new Intent(context, NewHomeActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
@Override
public void onCreate(Bundle savedInstanceState) {
SharedPreferences sharedPreferences = getSharedPreferences(LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE);
super.onCreate(savedInstanceState);
showMenuKey();
mUiHandler = new Handler(getMainLooper());
alertForMeizu();
alertForDonate();
mDirectlyBack = sharedPreferences.getBoolean(SettingsActivity.DIRECTLY_BACK_KEY, false);
}
private void installXposed() {
if (!VirtualCore.get().isXposedEnabled()) {
return;
}
boolean isXposedInstalled = false;
try {
isXposedInstalled = VirtualCore.get().isAppInstalled(XPOSED_INSTALLER_PACKAGE);
File oldXposedInstallerApk = getFileStreamPath("XposedInstaller_1_31.apk");
if (oldXposedInstallerApk.exists()) {
VirtualCore.get().uninstallPackage(XPOSED_INSTALLER_PACKAGE);
oldXposedInstallerApk.delete();
isXposedInstalled = false;
Log.d(TAG, "remove xposed installer success!");
}
} catch (Throwable e) {
VLog.d(TAG, "remove xposed install failed.", e);
}
if (!isXposedInstalled) {
ProgressDialog dialog = new ProgressDialog(this);
dialog.setCancelable(false);
dialog.setMessage(getResources().getString(R.string.prepare_xposed_installer));
dialog.show();
VUiKit.defer().when(() -> {
File xposedInstallerApk = getFileStreamPath("XposedInstaller_5_8.apk");
if (!xposedInstallerApk.exists()) {
InputStream input = null;
OutputStream output = null;
try {
input = getApplicationContext().getAssets().open("XposedInstaller_3.1.5.apk_");
output = new FileOutputStream(xposedInstallerApk);
byte[] buffer = new byte[1024];
int length;
while ((length = input.read(buffer)) > 0) {
output.write(buffer, 0, length);
}
} catch (Throwable e) {
VLog.e(TAG, "copy file error", e);
} finally {
FileUtils.closeQuietly(input);
FileUtils.closeQuietly(output);
}
}
if (xposedInstallerApk.isFile() && !DeviceUtil.isMeizuBelowN()) {
try {
if ("8537fb219128ead3436cc19ff35cfb2e".equals(MD5Utils.getFileMD5String(xposedInstallerApk))) {
VirtualCore.get().installPackage(xposedInstallerApk.getPath(), InstallStrategy.TERMINATE_IF_EXIST);
} else {
VLog.w(TAG, "unknown Xposed installer, ignore!");
}
} catch (Throwable ignored) {
}
}
}).then((v) -> {
dismissDialog(dialog);
}).fail((err) -> {
dismissDialog(dialog);
});
}
}
private static void dismissDialog(ProgressDialog dialog) {
if (dialog == null) {
return;
}
try {
dialog.dismiss();
} catch (Throwable e) {
e.printStackTrace();
}
}
@Override
protected void onResume() {
super.onResume();
if (checkXposedInstaller.compareAndSet(true, false)) {
installXposed();
}
// check for update
new Handler().postDelayed(() ->
VAVersionService.checkUpdate(getApplicationContext(), false), 1000);
// check for wallpaper
setWallpaper();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU) {
onSettingsClicked();
return true;
}
return super.onKeyDown(keyCode, event);
}
public Activity getActivity() {
return this;
}
public Context getContext() {
return this;
}
@Override
public void onClickAddWidgetButton(View view) {
onAddAppClicked();
}
private void onAddAppClicked() {
ListAppActivity.gotoListApp(this);
}
private void onSettingsClicked() {
startActivity(new Intent(NewHomeActivity.this, SettingsActivity.class));
}
@Override
public void onClickSettingsButton(View v) {
onSettingsClicked();
}
@Override
protected void onClickAllAppsButton(View v) {
onSettingsClicked();
}
@Override
public void startVirtualActivity(Intent intent, Bundle options, int usedId) {
String packageName = intent.getPackage();
if (TextUtils.isEmpty(packageName)) {
ComponentName component = intent.getComponent();
if (component != null) {
packageName = component.getPackageName();
}
}
if (packageName == null) {
try {
startActivity(intent);
return;
} catch (Throwable ignored) {
// ignore
}
}
boolean result = LoadingActivity.launch(this, packageName, usedId);
if (!result) {
throw new ActivityNotFoundException("can not launch activity for :" + intent);
}
if (mDirectlyBack) {
finish();
}
}
private void alertForDonate() {
final String TAG = "show_donate";
if (Once.beenDone(Once.THIS_APP_VERSION, TAG)) {
alertForDoze();
return;
}
AlertDialog alertDialog = new AlertDialog.Builder(getContext())
.setTitle(R.string.about_donate)
.setMessage(R.string.donate_dialog_content)
.setPositiveButton(android.R.string.yes, (dialog, which) -> {
Misc.showDonate(this);
Once.markDone(TAG);
})
.create();
try {
alertDialog.show();
} catch (Throwable ignored) {
}
}
private void alertForMeizu() {
if (!DeviceUtil.isMeizuBelowN()) {
return;
}
boolean isXposedInstalled = VirtualCore.get().isAppInstalled(XPOSED_INSTALLER_PACKAGE);
if (isXposedInstalled) {
return;
}
mUiHandler.postDelayed(() -> {
AlertDialog alertDialog = new AlertDialog.Builder(getContext())
.setTitle(R.string.meizu_device_tips_title)
.setMessage(R.string.meizu_device_tips_content)
.setPositiveButton(android.R.string.yes, (dialog, which) -> {
})
.create();
try {
alertDialog.show();
} catch (Throwable ignored) {
}
}, 2000);
}
private void alertForDoze() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return;
}
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
if (powerManager == null) {
return;
}
boolean showAlert = PreferenceManager.getDefaultSharedPreferences(this).getBoolean(SHOW_DOZE_ALERT_KEY, true);
if (!showAlert) {
return;
}
String packageName = getPackageName();
boolean ignoringBatteryOptimizations = powerManager.isIgnoringBatteryOptimizations(packageName);
if (!ignoringBatteryOptimizations) {
mUiHandler.postDelayed(() -> {
AlertDialog alertDialog = new AlertDialog.Builder(getContext())
.setTitle(R.string.alert_for_doze_mode_title)
.setMessage(R.string.alert_for_doze_mode_content)
.setPositiveButton(R.string.alert_for_doze_mode_yes, (dialog, which) -> {
try {
startActivity(new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, Uri.parse("package:" + getPackageName())));
} catch (ActivityNotFoundException ignored) {
// ActivityNotFoundException on some devices.
try {
startActivity(new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS));
} catch (Throwable e) {
PreferenceManager.getDefaultSharedPreferences(getActivity())
.edit().putBoolean(SHOW_DOZE_ALERT_KEY, false).apply();
}
} catch (Throwable e) {
PreferenceManager.getDefaultSharedPreferences(getActivity())
.edit().putBoolean(SHOW_DOZE_ALERT_KEY, false).apply();
}
})
.setNegativeButton(R.string.alert_for_doze_mode_no, (dialog, which) ->
PreferenceManager.getDefaultSharedPreferences(getActivity())
.edit().putBoolean(SHOW_DOZE_ALERT_KEY, false).apply())
.create();
try {
alertDialog.show();
} catch (Throwable ignored) {
ignored.printStackTrace();
}
}, 1000);
}
}
private void setWallpaper() {
File wallpaper = getFileStreamPath(WALLPAPER_FILE_NAME);
if (wallpaper == null || !wallpaper.exists() || wallpaper.isDirectory()) {
setOurWallpaper(getResources().getDrawable(R.drawable.home_bg));
} else {
long start = SystemClock.elapsedRealtime();
Drawable d;
try {
d = BitmapDrawable.createFromPath(wallpaper.getPath());
} catch (Throwable e) {
Toast.makeText(getApplicationContext(), R.string.wallpaper_too_big_tips, Toast.LENGTH_SHORT).show();
return;
}
long cost = SystemClock.elapsedRealtime() - start;
if (cost > 200) {
Toast.makeText(getApplicationContext(), R.string.wallpaper_too_big_tips, Toast.LENGTH_SHORT).show();
}
if (d == null) {
setOurWallpaper(getResources().getDrawable(R.drawable.home_bg));
} else {
setOurWallpaper(d);
}
}
}
private void showMenuKey() {
try {
Method setNeedsMenuKey = Window.class.getDeclaredMethod("setNeedsMenuKey", int.class);
setNeedsMenuKey.setAccessible(true);
int value = WindowManager.LayoutParams.class.getField("NEEDS_MENU_SET_TRUE").getInt(null);
setNeedsMenuKey.invoke(getWindow(), value);
} catch (Exception e) {
e.printStackTrace();
}
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/home/adapters/AppPagerAdapter.java
================================================
package io.virtualapp.home.adapters;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import io.virtualapp.R;
import io.virtualapp.XApp;
import io.virtualapp.home.ListAppFragment;
/**
* @author Lody
*/
public class AppPagerAdapter extends FragmentPagerAdapter {
private List titles = new ArrayList<>();
private List dirs = new ArrayList<>();
public AppPagerAdapter(FragmentManager fm) {
super(fm);
titles.add(XApp.getApp().getResources().getString(R.string.clone_apps));
dirs.add(null);
}
@Override
public Fragment getItem(int position) {
return ListAppFragment.newInstance(dirs.get(position));
}
@Override
public int getCount() {
return titles.size();
}
@Override
public CharSequence getPageTitle(int position) {
return titles.get(position);
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/home/adapters/CloneAppListAdapter.java
================================================
package io.virtualapp.home.adapters;
import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.io.File;
import java.util.List;
import io.virtualapp.R;
import io.virtualapp.abs.ui.VUiKit;
import io.virtualapp.glide.GlideUtils;
import io.virtualapp.home.models.AppInfo;
import io.virtualapp.widgets.DragSelectRecyclerViewAdapter;
import io.virtualapp.widgets.LabelView;
/**
* @author Lody
*/
public class CloneAppListAdapter extends DragSelectRecyclerViewAdapter {
private static final int TYPE_FOOTER = -2;
private final View mFooterView;
private LayoutInflater mInflater;
private List mAppList;
private ItemEventListener mItemEventListener;
private Context mContext;
private File mFrom;
public CloneAppListAdapter(Context context, @Nullable File from) {
mContext = context;
mFrom = from;
this.mInflater = LayoutInflater.from(context);
mFooterView = new View(context);
StaggeredGridLayoutManager.LayoutParams params = new StaggeredGridLayoutManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, VUiKit.dpToPx(context, 60)
);
params.setFullSpan(true);
mFooterView.setLayoutParams(params);
}
public void setOnItemClickListener(ItemEventListener mItemEventListener) {
this.mItemEventListener = mItemEventListener;
}
public List getList() {
return mAppList;
}
public void setList(List models) {
this.mAppList = models;
notifyDataSetChanged();
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_FOOTER) {
return new ViewHolder(mFooterView);
}
return new ViewHolder(mInflater.inflate(R.layout.item_clone_app, null));
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
if (getItemViewType(position) == TYPE_FOOTER) {
return;
}
super.onBindViewHolder(holder, position);
AppInfo info = mAppList.get(position);
if (mFrom == null) {
GlideUtils.loadInstalledPackageIcon(mContext, info.packageName, holder.iconView, android.R.drawable.sym_def_app_icon);
} else {
GlideUtils.loadPackageIconFromApkFile(mContext, info.path, holder.iconView, android.R.drawable.sym_def_app_icon);
}
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);
} else {
holder.iconView.setAlpha(0.65f);
holder.appCheckView.setImageResource(R.drawable.ic_no_check);
}
if (info.cloneCount > 0) {
holder.labelView.setVisibility(View.VISIBLE);
holder.labelView.setText(info.cloneCount + 1 + "");
} else {
holder.labelView.setVisibility(View.INVISIBLE);
}
holder.itemView.setOnClickListener(v -> {
mItemEventListener.onItemClick(info, position);
});
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
@Override
protected boolean isIndexSelectable(int index) {
return mItemEventListener.isSelectable(index);
}
@Override
public int getItemCount() {
return mAppList == null ? 1 : mAppList.size() + 1;
}
@Override
public int getItemViewType(int position) {
if (position == getItemCount() - 1) {
return TYPE_FOOTER;
}
return super.getItemViewType(position);
}
public AppInfo getItem(int index) {
return mAppList.get(index);
}
public interface ItemEventListener {
void onItemClick(AppInfo appData, int position);
boolean isSelectable(int position);
}
class ViewHolder extends RecyclerView.ViewHolder {
private ImageView iconView;
private TextView nameView;
private ImageView appCheckView;
private LabelView labelView;
ViewHolder(View itemView) {
super(itemView);
if (itemView != mFooterView) {
iconView = (ImageView) itemView.findViewById(R.id.item_app_icon);
nameView = (TextView) itemView.findViewById(R.id.item_app_name);
appCheckView = (ImageView) itemView.findViewById(R.id.item_app_checked);
labelView = (LabelView) itemView.findViewById(R.id.item_app_clone_count);
}
}
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/home/models/AppData.java
================================================
package io.virtualapp.home.models;
import android.graphics.drawable.Drawable;
/**
* @author Lody
*/
public interface AppData {
boolean isInstalling();
boolean isLoading();
boolean isFirstOpen();
Drawable getIcon();
String getName();
boolean canReorder();
boolean canLaunch();
boolean canDelete();
boolean canCreateShortcut();
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/home/models/AppInfo.java
================================================
package io.virtualapp.home.models;
import android.graphics.drawable.Drawable;
/**
* @author Lody
*/
public class AppInfo {
public String packageName;
public String path;
public boolean fastOpen;
public Drawable icon;
public CharSequence name;
public CharSequence version;
public int cloneCount;
public boolean disableMultiVersion;
public boolean splitApk;
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/home/models/AppInfoLite.java
================================================
package io.virtualapp.home.models;
import android.os.Parcel;
import android.os.Parcelable;
/**
* @author Lody
*/
public class AppInfoLite implements Parcelable {
public static final Creator CREATOR = new Creator() {
@Override
public AppInfoLite createFromParcel(Parcel source) {
return new AppInfoLite(source);
}
@Override
public AppInfoLite[] newArray(int size) {
return new AppInfoLite[size];
}
};
public String packageName;
public String path;
public boolean fastOpen;
public boolean disableMultiVersion;
public AppInfoLite(String packageName, String path, boolean fastOpen, boolean disableMultiVersion) {
this.packageName = packageName;
this.path = path;
this.fastOpen = fastOpen;
this.disableMultiVersion = disableMultiVersion;
}
protected AppInfoLite(Parcel in) {
this.packageName = in.readString();
this.path = in.readString();
this.fastOpen = in.readByte() != 0;
this.disableMultiVersion = in.readByte() != 0;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.packageName);
dest.writeString(this.path);
dest.writeByte(this.fastOpen ? (byte) 1 : (byte) 0);
dest.writeByte(this.disableMultiVersion ? (byte) 1 : (byte) 0);
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/home/models/MultiplePackageAppData.java
================================================
package io.virtualapp.home.models;
import android.graphics.drawable.Drawable;
import com.lody.virtual.client.core.VirtualCore;
import com.lody.virtual.remote.InstalledAppInfo;
/**
* @author Lody
*/
public class MultiplePackageAppData implements AppData {
public InstalledAppInfo appInfo;
public int userId;
public boolean isFirstOpen;
public boolean isInstalling;
public boolean isLoading;
public Drawable icon;
public String name;
public MultiplePackageAppData(PackageAppData target, int userId) {
this.userId = userId;
this.appInfo = VirtualCore.get().getInstalledAppInfo(target.packageName, 0);
this.isFirstOpen = !appInfo.isLaunched(userId);
if (target.icon != null) {
Drawable.ConstantState state = target.icon.getConstantState();
if (state != null) {
icon = state.newDrawable();
}
}
name = target.name;
}
@Override
public boolean isInstalling() {
return isInstalling;
}
@Override
public boolean isLoading() {
return isLoading;
}
@Override
public boolean isFirstOpen() {
return isFirstOpen;
}
@Override
public Drawable getIcon() {
return icon;
}
@Override
public String getName() {
return name;
}
@Override
public boolean canReorder() {
return true;
}
@Override
public boolean canLaunch() {
return true;
}
@Override
public boolean canDelete() {
return true;
}
@Override
public boolean canCreateShortcut() {
return true;
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/home/models/PackageAppData.java
================================================
package io.virtualapp.home.models;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import com.lody.virtual.remote.InstalledAppInfo;
/**
* @author Lody
*/
public class PackageAppData implements AppData {
public String packageName;
public String name;
public Drawable icon;
public boolean fastOpen;
public boolean isFirstOpen;
public boolean isLoading;
public boolean isInstalling;
public PackageAppData(Context context, InstalledAppInfo installedAppInfo) {
this.packageName = installedAppInfo.packageName;
this.isFirstOpen = !installedAppInfo.isLaunched(0);
loadData(context, installedAppInfo.getApplicationInfo(installedAppInfo.getInstalledUsers()[0]));
}
public PackageAppData(Context context, ApplicationInfo appInfo) {
this.packageName = appInfo.packageName;
loadData(context, appInfo);
}
private void loadData(Context context, ApplicationInfo appInfo) {
if (appInfo == null) {
return;
}
PackageManager pm = context.getPackageManager();
try {
CharSequence sequence = appInfo.loadLabel(pm);
if (sequence != null) {
name = sequence.toString();
}
icon = appInfo.loadIcon(pm);
} catch (Throwable e) {
e.printStackTrace();
}
}
@Override
public boolean isInstalling() {
return isInstalling;
}
@Override
public boolean isLoading() {
return isLoading;
}
@Override
public boolean isFirstOpen() {
return isFirstOpen;
}
@Override
public Drawable getIcon() {
return icon;
}
@Override
public String getName() {
return name;
}
@Override
public boolean canReorder() {
return true;
}
@Override
public boolean canLaunch() {
return true;
}
@Override
public boolean canDelete() {
return true;
}
@Override
public boolean canCreateShortcut() {
return true;
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/home/repo/AppDataSource.java
================================================
package io.virtualapp.home.repo;
import android.content.Context;
import com.lody.virtual.remote.InstallResult;
import org.jdeferred.Promise;
import java.io.File;
import java.util.List;
import io.virtualapp.home.models.AppData;
import io.virtualapp.home.models.AppInfo;
import io.virtualapp.home.models.AppInfoLite;
/**
* @author Lody
* @version 1.0
*/
public interface AppDataSource {
/**
* @return All the Applications we Virtual.
*/
Promise, Throwable, Void> getVirtualApps();
/**
* @param context Context
* @return All the Applications we Installed.
*/
Promise, Throwable, Void> getInstalledApps(Context context);
Promise, Throwable, Void> getStorageApps(Context context, File rootDir);
InstallResult addVirtualApp(AppInfoLite info);
boolean removeVirtualApp(String packageName, int userId);
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/home/repo/AppRepository.java
================================================
package io.virtualapp.home.repo;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import com.lody.virtual.GmsSupport;
import com.lody.virtual.client.core.InstallStrategy;
import com.lody.virtual.client.core.VirtualCore;
import com.lody.virtual.helper.utils.DeviceUtil;
import com.lody.virtual.remote.InstallResult;
import com.lody.virtual.remote.InstalledAppInfo;
import org.jdeferred.Promise;
import java.io.File;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import io.virtualapp.abs.ui.VUiKit;
import io.virtualapp.home.models.AppData;
import io.virtualapp.home.models.AppInfo;
import io.virtualapp.home.models.AppInfoLite;
import io.virtualapp.home.models.MultiplePackageAppData;
import io.virtualapp.home.models.PackageAppData;
import io.virtualapp.utils.HanziToPinyin;
/**
* @author Lody
*/
public class AppRepository implements AppDataSource {
private static final Collator COLLATOR = Collator.getInstance(Locale.CHINA);
private static final List SCAN_PATH_LIST = Arrays.asList(
".",
"wandoujia/app",
"tencent/tassistant/apk",
"BaiduAsa9103056",
"360Download",
"pp/downloader",
"pp/downloader/apk",
"pp/downloader/silent/apk");
private static final int MAX_SCAN_DEPTH = 2;
private Context mContext;
public AppRepository(Context context) {
mContext = context;
}
private static boolean isSystemApplication(PackageInfo packageInfo) {
return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
&& !GmsSupport.isGmsFamilyPackage(packageInfo.packageName);
}
@Override
public Promise, Throwable, Void> getVirtualApps() {
return VUiKit.defer().when(() -> {
List infos = VirtualCore.get().getInstalledApps(0);
List models = new ArrayList<>();
for (InstalledAppInfo info : infos) {
if (!VirtualCore.get().isPackageLaunchable(info.packageName)) {
continue;
}
PackageAppData data = new PackageAppData(mContext, info);
if (VirtualCore.get().isAppInstalledAsUser(0, info.packageName)) {
models.add(data);
}
int[] userIds = info.getInstalledUsers();
for (int userId : userIds) {
if (userId != 0) {
models.add(new MultiplePackageAppData(data, userId));
}
}
}
return models;
});
}
@Override
public Promise, Throwable, Void> getInstalledApps(Context context) {
return VUiKit.defer().when(() -> convertPackageInfoToAppData(context, context.getPackageManager().getInstalledPackages(PackageManager.GET_META_DATA), true));
}
@Override
public Promise, Throwable, Void> getStorageApps(Context context, File rootDir) {
// return VUiKit.defer().when(() -> convertPackageInfoToAppData(context, findAndParseAPKs(context, rootDir, SCAN_PATH_LIST), false));
return VUiKit.defer().when(() -> convertPackageInfoToAppData(context, findAndParseApkRecursively(context, rootDir,null, 0), false));
}
private List findAndParseApkRecursively(Context context, File rootDir, List result, int depth) {
if (result == null) {
result = new ArrayList<>();
}
if (depth > MAX_SCAN_DEPTH) {
return result;
}
File[] dirFiles = rootDir.listFiles();
if (dirFiles == null) {
return Collections.emptyList();
}
for (File f: dirFiles) {
if (f.isDirectory()) {
List andParseApkRecursively = findAndParseApkRecursively(context, f, new ArrayList<>(), depth + 1);
result.addAll(andParseApkRecursively);
}
if (!(f.isFile() && f.getName().toLowerCase().endsWith(".apk"))) {
continue;
}
PackageInfo pkgInfo = null;
try {
pkgInfo = context.getPackageManager().getPackageArchiveInfo(f.getAbsolutePath(), PackageManager.GET_META_DATA);
pkgInfo.applicationInfo.sourceDir = f.getAbsolutePath();
pkgInfo.applicationInfo.publicSourceDir = f.getAbsolutePath();
} catch (Exception e) {
// Ignore
}
if (pkgInfo != null) {
result.add(pkgInfo);
}
}
return result;
}
private List findAndParseAPKs(Context context, File rootDir, List paths) {
List packageList = new ArrayList<>();
if (paths == null)
return packageList;
for (String path : paths) {
File[] dirFiles = new File(rootDir, path).listFiles();
if (dirFiles == null)
continue;
for (File f : dirFiles) {
if (!f.getName().toLowerCase().endsWith(".apk"))
continue;
PackageInfo pkgInfo = null;
try {
pkgInfo = context.getPackageManager().getPackageArchiveInfo(f.getAbsolutePath(), 0);
pkgInfo.applicationInfo.sourceDir = f.getAbsolutePath();
pkgInfo.applicationInfo.publicSourceDir = f.getAbsolutePath();
} catch (Exception e) {
// Ignore
}
if (pkgInfo != null)
packageList.add(pkgInfo);
}
}
return packageList;
}
private List convertPackageInfoToAppData(Context context, List pkgList, boolean fastOpen) {
PackageManager pm = context.getPackageManager();
List list = new ArrayList<>(pkgList.size());
String hostPkg = VirtualCore.get().getHostPkg();
for (PackageInfo pkg : pkgList) {
// ignore the host package
if (hostPkg.equals(pkg.packageName)) {
continue;
}
// ignore taichi package
if (VirtualCore.TAICHI_PACKAGE.equals(pkg.packageName)) {
continue;
}
// ignore the System package
if (isSystemApplication(pkg)) {
continue;
}
ApplicationInfo ai = pkg.applicationInfo;
String path = ai.publicSourceDir != null ? ai.publicSourceDir : ai.sourceDir;
boolean splitApk = false;
if (ai.splitPublicSourceDirs != null || ai.splitSourceDirs != null) {
splitApk = true;
path = new File(path).getParent();
}
if (path == null) {
continue;
}
AppInfo info = new AppInfo();
info.packageName = pkg.packageName;
info.fastOpen = fastOpen;
info.path = path;
info.icon = null; // Use Glide to load the icon async
info.name = ai.loadLabel(pm);
info.version = pkg.versionName;
info.splitApk = splitApk;
InstalledAppInfo installedAppInfo = VirtualCore.get().getInstalledAppInfo(pkg.packageName, 0);
if (installedAppInfo != null) {
info.cloneCount = installedAppInfo.getInstalledUsers().length;
}
if (ai.metaData != null && ai.metaData.containsKey("xposedmodule")) {
info.disableMultiVersion = true;
info.cloneCount = 0;
}
list.add(info);
}
// sort by name
Collections.sort(list, (o1, o2) -> {
HanziToPinyin hanziToPinyin = HanziToPinyin.getInstance();
String pinyin1 = hanziToPinyin.toPinyinString(o1.name.toString().trim());
String pinyin2 = hanziToPinyin.toPinyinString(o2.name.toString().trim());
return pinyin1.compareTo(pinyin2);
});
return list;
}
@Override
public InstallResult addVirtualApp(AppInfoLite info) {
int flags = InstallStrategy.COMPARE_VERSION | InstallStrategy.SKIP_DEX_OPT;
info.fastOpen = false; // disable fast open for compile.
if (DeviceUtil.isMeizuBelowN()) {
info.fastOpen = true;
}
if (info.fastOpen) {
flags |= InstallStrategy.DEPEND_SYSTEM_IF_EXIST;
}
if (info.disableMultiVersion) {
flags |= InstallStrategy.UPDATE_IF_EXIST;
}
return VirtualCore.get().installPackage(info.path, flags);
}
@Override
public boolean removeVirtualApp(String packageName, int userId) {
return VirtualCore.get().uninstallPackageAsUser(packageName, userId);
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/home/repo/PackageAppDataStorage.java
================================================
package io.virtualapp.home.repo;
import android.content.pm.ApplicationInfo;
import com.lody.virtual.client.core.VirtualCore;
import com.lody.virtual.remote.InstalledAppInfo;
import java.util.HashMap;
import java.util.Map;
import io.virtualapp.XApp;
import io.virtualapp.abs.Callback;
import io.virtualapp.abs.ui.VUiKit;
import io.virtualapp.home.models.PackageAppData;
/**
* @author Lody
*
* Currently this file is aligned to zh.txt in ICU 4.6
*/
public class HanziToPinyin {
private static final String TAG = "HanziToPinyin";
// Turn on this flag when we want to check internal data structure.
private static final boolean DEBUG = false;
/**
* Unihans array.
*
* {@link #getCount()} - Decides the number of views present in the view
*
* {@link #createView(int, ViewGroup)} - Creates the view for all positions in
* range [0, {@link #getCount()})
*
* Contains the logic for touch events in {@link #onTouch(View, MotionEvent)}
*/
public abstract class CardStackAdapter implements View.OnTouchListener, View.OnClickListener {
public static final int ANIM_DURATION = 600;
public static final int DECELERATION_FACTOR = 2;
public static final int INVALID_CARD_POSITION = -1;
private final int mScreenHeight;
private final int dp30;
// Settings for the adapter from layout
private float mCardGapBottom;
private float mCardGap;
private int mParallaxScale;
private boolean mParallaxEnabled;
private boolean mShowInitAnimation;
private int fullCardHeight;
private View[] mCardViews;
private float dp8;
private CardStackLayout mParent;
private boolean mScreenTouchable = false;
private float mTouchFirstY = -1;
private float mTouchPrevY = -1;
private float mTouchDistance = 0;
private int mSelectedCardPosition = INVALID_CARD_POSITION;
private float scaleFactorForElasticEffect;
private int mParentPaddingTop = 0;
private int mCardPaddingInternal = 0;
public CardStackAdapter(Context context) {
Resources resources = context.getResources();
DisplayMetrics dm = Resources.getSystem().getDisplayMetrics();
mScreenHeight = dm.heightPixels;
dp30 = (int) resources.getDimension(R.dimen.dp30);
scaleFactorForElasticEffect = (int) resources.getDimension(R.dimen.dp8);
dp8 = (int) resources.getDimension(R.dimen.dp8);
}
protected float getCardGapBottom() {
return mCardGapBottom;
}
/**
* Defines and initializes the view to be shown in the
* {@link CardStackLayout} Provides two parameters to the sub-class namely -
*
* @param position
* @param container
* @return View corresponding to the position and parent container
*/
public abstract View createView(int position, ViewGroup container);
/**
* Defines the number of cards that are present in the
* {@link CardStackLayout}
*
* @return cardCount - Number of views in the related
* {@link CardStackLayout}
*/
public abstract int getCount();
/**
* Returns true if no animation is in progress currently. Can be used to
* disable any events if they are not allowed during an animation. Returns
* false if an animation is in progress.
*
* @return - true if animation in progress, false otherwise
*/
public boolean isScreenTouchable() {
return mScreenTouchable;
}
private void setScreenTouchable(boolean screenTouchable) {
this.mScreenTouchable = screenTouchable;
}
void addView(final int position) {
View root = createView(position, mParent);
root.setOnTouchListener(this);
root.setTag(R.id.cardstack_internal_position_tag, position);
root.setLayerType(View.LAYER_TYPE_HARDWARE, null);
mCardPaddingInternal = root.getPaddingTop();
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, fullCardHeight);
root.setLayoutParams(lp);
if (mShowInitAnimation) {
root.setY(getCardFinalY(position));
setScreenTouchable(false);
} else {
root.setY(getCardOriginalY(position) - mParentPaddingTop);
setScreenTouchable(true);
}
mCardViews[position] = root;
mParent.addView(root);
}
protected float getCardFinalY(int position) {
return mScreenHeight - dp30 - ((getCount() - position) * mCardGapBottom) - mCardPaddingInternal;
}
protected float getCardOriginalY(int position) {
return mParentPaddingTop + mCardGap * position;
}
/**
* Resets all cards in {@link CardStackLayout} to their initial positions
*
* @param r
* Execute r.run() once the reset animation is done
*/
public void resetCards(Runnable r) {
List animations = new ArrayList<>(getCount());
for (int i = 0; i < getCount(); i++) {
final View child = mCardViews[i];
animations.add(ObjectAnimator.ofFloat(child, View.Y, (int) child.getY(), getCardOriginalY(i)));
}
startAnimations(animations, r, true);
}
/**
* Plays together all animations passed in as parameter. Once animation is
* completed, r.run() is executed. If parameter isReset is set to true,
* {@link #mSelectedCardPosition} is set to {@link #INVALID_CARD_POSITION}
*
* @param animations
* @param r
* @param isReset
*/
private void startAnimations(List animations, final Runnable r, final boolean isReset) {
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(animations);
animatorSet.setDuration(ANIM_DURATION);
animatorSet.setInterpolator(new DecelerateInterpolator(DECELERATION_FACTOR));
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (r != null)
r.run();
setScreenTouchable(true);
if (isReset)
mSelectedCardPosition = INVALID_CARD_POSITION;
}
});
animatorSet.start();
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (!isScreenTouchable()) {
return false;
}
float y = event.getRawY();
int positionOfCardToMove = (int) v.getTag(R.id.cardstack_internal_position_tag);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN :
if (mTouchFirstY != -1) {
return false;
}
mTouchPrevY = mTouchFirstY = y;
mTouchDistance = 0;
break;
case MotionEvent.ACTION_MOVE :
if (mSelectedCardPosition == INVALID_CARD_POSITION)
moveCards(positionOfCardToMove, y - mTouchFirstY);
mTouchDistance += Math.abs(y - mTouchPrevY);
break;
case MotionEvent.ACTION_CANCEL :
case MotionEvent.ACTION_UP :
if (mTouchDistance < dp8 && Math.abs(y - mTouchFirstY) < dp8
&& mSelectedCardPosition == INVALID_CARD_POSITION) {
onClick(v);
} else {
resetCards();
}
mTouchPrevY = mTouchFirstY = -1;
mTouchDistance = 0;
return false;
}
return true;
}
@Override
public void onClick(final View v) {
if (!isScreenTouchable()) {
return;
}
setScreenTouchable(false);
if (mSelectedCardPosition == INVALID_CARD_POSITION) {
mSelectedCardPosition = (int) v.getTag(R.id.cardstack_internal_position_tag);
List animations = new ArrayList<>(getCount());
for (int i = 0; i < getCount(); i++) {
View child = mCardViews[i];
animations.add(getAnimatorForView(child, i, mSelectedCardPosition));
}
startAnimations(animations, () -> {
setScreenTouchable(true);
if (mParent.getOnCardSelectedListener() != null) {
mParent.getOnCardSelectedListener().onCardSelected(v, mSelectedCardPosition);
}
}, false);
}
}
/**
* This method can be overridden to have different animations for each card
* when a click event happens on any card view. This method will be called
* for every
*
* @param view
* The view for which this method needs to return an animator
* @param selectedCardPosition
* Position of the card that was clicked
* @param currentCardPosition
* Position of the current card
* @return animator which has to be applied on the current card
*/
protected Animator getAnimatorForView(View view, int currentCardPosition, int selectedCardPosition) {
if (currentCardPosition != selectedCardPosition) {
return ObjectAnimator.ofFloat(view, View.Y, (int) view.getY(), getCardFinalY(currentCardPosition));
} else {
return ObjectAnimator.ofFloat(view, View.Y, (int) view.getY(),
getCardOriginalY(0) + (currentCardPosition * mCardGapBottom));
}
}
private void moveCards(int positionOfCardToMove, float diff) {
if (diff < 0 || positionOfCardToMove < 0 || positionOfCardToMove >= getCount())
return;
for (int i = positionOfCardToMove; i < getCount(); i++) {
final View child = mCardViews[i];
float diffCard = diff / scaleFactorForElasticEffect;
if (mParallaxEnabled) {
if (mParallaxScale > 0) {
diffCard = diffCard * (mParallaxScale / 3) * (getCount() + 1 - i);
} else {
int scale = mParallaxScale * -1;
diffCard = diffCard * (i * (scale / 3) + 1);
}
} else
diffCard = diffCard * (getCount() * 2 + 1);
child.setY(getCardOriginalY(i) + diffCard);
}
}
/**
* Provides an API to {@link CardStackLayout} to set the parameters provided
* to it in its XML
*
* @param cardStackLayout
* Parent of all cards
*/
void setAdapterParams(CardStackLayout cardStackLayout) {
mParent = cardStackLayout;
mCardViews = new View[getCount()];
mCardGapBottom = cardStackLayout.getCardGapBottom();
mCardGap = cardStackLayout.getCardGap();
mParallaxScale = cardStackLayout.getParallaxScale();
mParallaxEnabled = cardStackLayout.isParallaxEnabled();
if (mParallaxEnabled && mParallaxScale == 0)
mParallaxEnabled = false;
mShowInitAnimation = cardStackLayout.isShowInitAnimation();
mParentPaddingTop = cardStackLayout.getPaddingTop();
fullCardHeight = (int) (mScreenHeight - dp30 - dp8 - getCount() * mCardGapBottom);
}
/**
* Resets all cards in {@link CardStackLayout} to their initial positions
*/
public void resetCards() {
resetCards(null);
}
/**
* Returns false if all the cards are in their initial position i.e. no card
* is selected
*
* Returns true if the {@link CardStackLayout} has a card selected and all
* other cards are at the bottom of the screen.
*
* @return true if any card is selected, false otherwise
*/
public boolean isCardSelected() {
return mSelectedCardPosition != INVALID_CARD_POSITION;
}
/**
* Returns the position of selected card. If no card is selected, returns
* {@link #INVALID_CARD_POSITION}
*/
public int getSelectedCardPosition() {
return mSelectedCardPosition;
}
/**
* Since there is no view recycling in {@link CardStackLayout}, we maintain
* an instance of every view that is set for every position. This method
* returns a view at the requested position.
*
* @param position
* Position of card in {@link CardStackLayout}
* @return View at requested position
*/
public View getCardView(int position) {
if (mCardViews == null)
return null;
return mCardViews[position];
}
}
================================================
FILE: VirtualApp/app/src/main/java/io/virtualapp/widgets/CardStackLayout.java
================================================
package io.virtualapp.widgets;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import io.virtualapp.R;
/**
* Displays a list of cards as a stack on the screen.
*
* XML attributes
*
* See {@link R.styleable#CardStackLayout CardStackLayout Attributes}
*
Two strings are provided in the extras bundle: EXTRA_EXISTING_PERMISSION
* is the name of the permission that the app is attempting to define, and
* EXTRA_EXISTING_PACKAGE is the package name of the app which has already
* defined the permission.
*
*
*/
void onPackageInstalled(String basePackageName, int returnCode, String msg, in Bundle extras);
}
================================================
FILE: VirtualApp/lib/src/main/aidl/android/content/pm/IPackageInstallerCallback.aidl
================================================
package android.content.pm;
interface IPackageInstallerCallback {
void onSessionCreated(int sessionId);
void onSessionBadgingChanged(int sessionId);
void onSessionActiveChanged(int sessionId, boolean active);
void onSessionProgressChanged(int sessionId, float progress);
void onSessionFinished(int sessionId, boolean success);
}
================================================
FILE: VirtualApp/lib/src/main/aidl/android/content/pm/IPackageInstallerSession.aidl
================================================
package android.content.pm;
import android.content.pm.IPackageInstallObserver2;
import android.content.IntentSender;
import android.os.ParcelFileDescriptor;
interface IPackageInstallerSession {
void setClientProgress(float progress);
void addClientProgress(float progress);
String[] getNames();
ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes);
ParcelFileDescriptor openRead(String name);
void removeSplit(String splitName);
void close();
void commit(in IntentSender statusReceiver);
void abandon();
}
================================================
FILE: VirtualApp/lib/src/main/aidl/android/location/ILocationListener.aidl
================================================
// ILocationListener.aidl
package android.location;
import android.location.Location;
import android.os.Bundle;
interface ILocationListener
{
void onLocationChanged(in Location location);
void onStatusChanged(String provider, int status, in Bundle extras);
void onProviderEnabled(String provider);
void onProviderDisabled(String provider);
}
================================================
FILE: VirtualApp/lib/src/main/aidl/android/net/IConnectivityManager.aidl
================================================
package android.net;
import android.net.NetworkInfo;
import android.net.LinkProperties;
interface IConnectivityManager {
NetworkInfo getActiveNetworkInfo();
NetworkInfo getActiveNetworkInfoForUid(int uid, boolean ignoreBlocked);
NetworkInfo getNetworkInfo(int networkType);
NetworkInfo[] getAllNetworkInfo();
boolean isActiveNetworkMetered();
boolean requestRouteToHostAddress(int networkType, int address);
LinkProperties getActiveLinkProperties();
LinkProperties getLinkProperties(int networkType);
}
================================================
FILE: VirtualApp/lib/src/main/aidl/android/net/wifi/IWifiScanner.aidl
================================================
package android.net.wifi;
import android.os.Messenger;
import android.os.Bundle;
interface IWifiScanner
{
Messenger getMessenger();
Bundle getAvailableChannels(int band);
}
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/client/IVClient.aidl
================================================
// IVClient.aidl
package com.lody.virtual.client;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ProviderInfo;
import com.lody.virtual.remote.PendingResultData;
interface IVClient {
void scheduleReceiver(in String processName,in ComponentName component, in Intent intent, in PendingResultData resultData);
void scheduleNewIntent(in String creator, in IBinder token, in Intent intent);
void finishActivity(in IBinder token);
IBinder createProxyService(in ComponentName component, in IBinder binder);
IBinder acquireProviderClient(in ProviderInfo info);
IBinder getAppThread();
IBinder getToken();
String getDebugInfo();
}
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/os/VUserInfo.aidl
================================================
// VUserInfo.aidl
package com.lody.virtual.os;
parcelable VUserInfo;
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/AppTaskInfo.aidl
================================================
// AppTaskInfo.aidl
package com.lody.virtual.remote;
parcelable AppTaskInfo;
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/BadgerInfo.aidl
================================================
// BadgerInfo.aidl
package com.lody.virtual.remote;
parcelable BadgerInfo;
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/InstallResult.aidl
================================================
// InstallResult.aidl
package com.lody.virtual.remote;
parcelable InstallResult;
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/InstalledAppInfo.aidl
================================================
// AppSetting.aidl
package com.lody.virtual.remote;
parcelable InstalledAppInfo;
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/PendingIntentData.aidl
================================================
// PendingIntentData.aidl
package com.lody.virtual.remote;
parcelable PendingIntentData;
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/PendingResultData.aidl
================================================
// PendingResultData.aidl
package com.lody.virtual.remote;
parcelable PendingResultData;
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/Problem.aidl
================================================
// Problem.aidl
package com.lody.virtual.remote;
parcelable Problem;
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/ReceiverInfo.aidl
================================================
// ReceiverInfo.aidl
package com.lody.virtual.remote;
parcelable ReceiverInfo;
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/VDeviceInfo.aidl
================================================
// VDeviceInfo.aidl
package com.lody.virtual.remote;
parcelable VDeviceInfo;
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/VParceledListSlice.aidl
================================================
// VParceledListSlice.aidl
package com.lody.virtual.remote;
parcelable VParceledListSlice;
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/vloc/VCell.aidl
================================================
// VCell.aidl
package com.lody.virtual.remote.vloc;
parcelable VCell;
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/vloc/VLocation.aidl
================================================
// VLocation.aidl
package com.lody.virtual.remote.vloc;
parcelable VLocation;
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/vloc/VWifi.aidl
================================================
// VWifi.aidl
package com.lody.virtual.remote.vloc;
parcelable VWifi;
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IAccountManager.aidl
================================================
package com.lody.virtual.server;
import android.accounts.IAccountManagerResponse;
import android.accounts.Account;
import android.accounts.AuthenticatorDescription;
import android.os.Bundle;
/**
* Central application service that provides account management.
* @hide
*/
interface IAccountManager {
AuthenticatorDescription[] getAuthenticatorTypes(int userId);
void getAccountsByFeatures(int userId, in IAccountManagerResponse response, in String type, in String[] features);
String getPreviousName(int userId, in Account account);
Account[] getAccounts(int userId, in String type);
void getAuthToken(int userId, in IAccountManagerResponse response, in Account account, in String authTokenType, in boolean notifyOnAuthFailure, in boolean expectActivityLaunch, in Bundle loginOptions);
void setPassword(int userId, in Account account, in String password);
void setAuthToken(int userId, in Account account, in String authTokenType, in String authToken);
void setUserData(int userId, in Account account, in String key, in String value);
void hasFeatures(int userId, in IAccountManagerResponse response,
in Account account, in String[] features);
void updateCredentials(int userId, in IAccountManagerResponse response, in Account account,
in String authTokenType, in boolean expectActivityLaunch,
in Bundle loginOptions);
void editProperties(int userId, in IAccountManagerResponse response, in String accountType,
in boolean expectActivityLaunch);
void getAuthTokenLabel(int userId, in IAccountManagerResponse response, in String accountType,
in String authTokenType);
String getUserData(int userId, in Account account, in String key);
String getPassword(int userId, in Account account);
void confirmCredentials(int userId, in IAccountManagerResponse response, in Account account, in Bundle options, in boolean expectActivityLaunch);
void addAccount(int userId, in IAccountManagerResponse response, in String accountType,
in String authTokenType, in String[] requiredFeatures,
in boolean expectActivityLaunch, in Bundle optionsIn);
boolean addAccountExplicitly(int userId, in Account account, in String password, in Bundle extras);
boolean removeAccountExplicitly(int userId, in Account account);
void renameAccount(int userId, in IAccountManagerResponse response, in Account accountToRename, in String newName);
void removeAccount(in int userId, in IAccountManagerResponse response, in Account account,
in boolean expectActivityLaunch);
void clearPassword(int userId, in Account account);
boolean accountAuthenticated(int userId, in Account account);
void invalidateAuthToken(int userId, in String accountType, in String authToken);
String peekAuthToken(int userId, in Account account, in String authTokenType);
}
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IActivityManager.aidl
================================================
// IActivityManager.aidl
package com.lody.virtual.server;
import com.lody.virtual.remote.VParceledListSlice;
import com.lody.virtual.remote.AppTaskInfo;
import com.lody.virtual.remote.PendingIntentData;
import com.lody.virtual.remote.PendingResultData;
import com.lody.virtual.remote.BadgerInfo;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.app.Notification;
import android.app.IServiceConnection;
import android.app.IActivityManager.ContentProviderHolder;
import com.lody.virtual.server.interfaces.IProcessObserver;
interface IActivityManager {
int initProcess(in String packageName, in String processName, int userId);
int getFreeStubCount();
int getSystemPid();
int getUidByPid(int pid);
boolean isAppProcess(String processName);
boolean isAppRunning(String packageName, int userId);
boolean isAppPid(int pid);
String getAppProcessName(int pid);
List getProcessPkgList(int pid);
void killAllApps();
void killAppByPkg(String pkg, int userId);
void killApplicationProcess(String procName, int vuid);
void dump();
void registerProcessObserver(in IProcessObserver observer);
void unregisterProcessObserver(in IProcessObserver observer);
String getInitialPackage(int pid);
void handleApplicationCrash();
void appDoneExecuting();
int startActivities(in Intent[] intents, in String[] resolvedTypes, in IBinder token, in Bundle options, in int userId);
int startActivity(in Intent intent, in ActivityInfo info, in IBinder resultTo, in Bundle options, String resultWho, int requestCode, int userId);
void onActivityCreated(in ComponentName component, in ComponentName caller, in IBinder token, in Intent intent, in String affinity, int taskId, int launchMode, int flags);
void onActivityResumed(int userId, in IBinder token);
boolean onActivityDestroyed(int userId, in IBinder token);
ComponentName getActivityClassForToken(int userId, in IBinder token);
String getCallingPackage(int userId, in IBinder token);
ComponentName getCallingActivity(int userId, in IBinder token);
AppTaskInfo getTaskInfo(int taskId);
String getPackageForToken(int userId, in IBinder token);
boolean isVAServiceToken(in IBinder token);
ComponentName startService(in IBinder caller,in Intent service, String resolvedType, int userId);
int stopService(in IBinder caller, in Intent service, String resolvedType, int userId);
boolean stopServiceToken(in ComponentName className, in IBinder token, int startId, int userId);
void setServiceForeground(in ComponentName className, in IBinder token, int id,
in Notification notification, boolean removeNotification, int userId);
int bindService(in IBinder caller, in IBinder token, in Intent service,
String resolvedType, in IServiceConnection connection, int flags, int userId);
boolean unbindService(in IServiceConnection connection, int userId);
void unbindFinished(in IBinder token, in Intent service, in boolean doRebind, int userId);
void serviceDoneExecuting(in IBinder token, in int type, in int startId, in int res, int userId);
IBinder peekService(in Intent service, String resolvedType, int userId);
void publishService(in IBinder token, in Intent intent, in IBinder service, int userId);
VParceledListSlice getServices(int maxNum, int flags, int userId);
IBinder acquireProviderClient(int userId, in ProviderInfo info);
PendingIntentData getPendingIntent(IBinder binder);
void addPendingIntent(IBinder binder, String packageName);
void removePendingIntent(IBinder binder);
String getPackageForIntentSender(IBinder binder);
void processRestarted(in String packageName, in String processName, int userId);
void broadcastFinish(in PendingResultData res);
void notifyBadgerChange(in BadgerInfo info);
}
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IAppManager.aidl
================================================
// IAppManager.aidl
package com.lody.virtual.server;
import com.lody.virtual.server.interfaces.IPackageObserver;
import com.lody.virtual.server.interfaces.IAppRequestListener;
import com.lody.virtual.remote.InstalledAppInfo;
import com.lody.virtual.remote.InstallResult;
interface IAppManager {
int[] getPackageInstalledUsers(String packageName);
void scanApps();
void addVisibleOutsidePackage(String pkg);
void removeVisibleOutsidePackage(String pkg);
boolean isOutsidePackageVisible(String pkg);
InstalledAppInfo getInstalledAppInfo(String pkg, int flags);
InstallResult installPackage(String path, int flags);
boolean isPackageLaunched(int userId, String packageName);
void setPackageHidden(int userId, String packageName, boolean hidden);
boolean installPackageAsUser(int userId, String packageName);
boolean uninstallPackageAsUser(String packageName, int userId);
boolean uninstallPackage(String packageName);
boolean clearPackageAsUser(int userId, String packageName);
boolean clearPackage(String packageName);
List getInstalledApps(int flags);
List getInstalledAppsAsUser(int userId, int flags);
int getInstalledAppCount();
boolean isAppInstalled(String packageName);
boolean isAppInstalledAsUser(int userId, String packageName);
void registerObserver(IPackageObserver observer);
void unregisterObserver(IPackageObserver observer);
void setAppRequestListener(IAppRequestListener listener);
void clearAppRequestListener();
IAppRequestListener getAppRequestListener();
}
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IBinderDelegateService.aidl
================================================
// IBinderDelegateService.aidl
package com.lody.virtual.server;
import android.content.ComponentName;
interface IBinderDelegateService {
ComponentName getComponent();
IBinder getService();
}
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IDeviceInfoManager.aidl
================================================
// IDeviceInfoManager.aidl
package com.lody.virtual.server;
import com.lody.virtual.remote.VDeviceInfo;
interface IDeviceInfoManager {
VDeviceInfo getDeviceInfo(int userId);
void updateDeviceInfo(int userId, in VDeviceInfo info);
}
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IJobScheduler.aidl
================================================
//package com.lody.virtual.server;
//
//import android.app.job.JobInfo;
//import android.app.job.JobParameters;
//
// /**
// * IPC interface that supports the app-facing {@link #JobScheduler} api.
// */
//interface IJobScheduler {
// int schedule(in JobInfo job);
// void cancel(int jobId);
// void cancelAll();
// List getAllPendingJobs();
// int enqueue(in JobInfo job, in JobParameters work);
// JobInfo getPendingJob(int i);
//}
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/server/INotificationManager.aidl
================================================
// INotificationManager.aidl
package com.lody.virtual.server;
// Declare any non-default types here with import statements
import android.app.Notification;
interface INotificationManager {
int dealNotificationId(int id, String packageName, String tag, int userId);
String dealNotificationTag(int id, String packageName, String tag, int userId);
boolean areNotificationsEnabledForPackage(String packageName, int userId);
void setNotificationsEnabledForPackage(String packageName, boolean enable, int userId);
void addNotification(int id, String tag, String packageName, int userId);
void cancelAllNotification(String packageName, int userId);
}
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IPackageInstaller.aidl
================================================
package com.lody.virtual.server;
import android.content.pm.IPackageDeleteObserver2;
import android.content.pm.IPackageInstallerCallback;
import android.content.pm.IPackageInstallerSession;
import android.content.IntentSender;
import android.graphics.Bitmap;
import com.lody.virtual.remote.VParceledListSlice;
import com.lody.virtual.server.pm.installer.SessionParams;
import com.lody.virtual.server.pm.installer.SessionInfo;
interface IPackageInstaller {
int createSession(in SessionParams params, String installerPackageName, int userId);
void updateSessionAppIcon(int sessionId, in Bitmap appIcon);
void updateSessionAppLabel(int sessionId, String appLabel);
void abandonSession(int sessionId);
IPackageInstallerSession openSession(int sessionId);
SessionInfo getSessionInfo(int sessionId);
VParceledListSlice getAllSessions(int userId);
VParceledListSlice getMySessions(String installerPackageName, int userId);
void registerCallback(IPackageInstallerCallback callback, int userId);
void unregisterCallback(IPackageInstallerCallback callback);
void uninstall(String packageName, String callerPackageName, int flags,
in IntentSender statusReceiver, int userId);
void setPermissionsResult(int sessionId, boolean accepted);
}
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IPackageInstallerSession.aidl
================================================
package com.lody.virtual.server;
import android.content.IntentSender;
import android.os.ParcelFileDescriptor;
interface IPackageInstallerSession {
void setClientProgress(float progress);
void addClientProgress(float progress);
String[] getNames();
ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes);
ParcelFileDescriptor openRead(String name);
void close();
void commit(in IntentSender statusReceiver);
void abandon();
}
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IPackageManager.aidl
================================================
// IPackageManager.aidl
package com.lody.virtual.server;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.ActivityInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ApplicationInfo;
import android.content.IntentFilter;
import android.content.pm.PermissionInfo;
import android.content.pm.PermissionGroupInfo;
import com.lody.virtual.remote.ReceiverInfo;
import com.lody.virtual.remote.VParceledListSlice;
import com.lody.virtual.server.IPackageInstaller;
interface IPackageManager {
int getPackageUid(String packageName, int userId);
String[] getPackagesForUid(int vuid);
List getSharedLibraries(String pkgName);
int checkPermission(String permName, String pkgName, int userId);
PackageInfo getPackageInfo(String packageName, int flags, int userId);
ActivityInfo getActivityInfo(in ComponentName componentName, int flags, int userId);
boolean activitySupportsIntent(in ComponentName component, in Intent intent,
in String resolvedType);
ActivityInfo getReceiverInfo(in ComponentName componentName, int flags, int userId);
ServiceInfo getServiceInfo(in ComponentName componentName, int flags, int userId);
ProviderInfo getProviderInfo(in ComponentName componentName, int flags, int userId);
ResolveInfo resolveIntent(in Intent intent, in String resolvedType, int flags, int userId);
List queryIntentActivities(in Intent intent,in String resolvedType, int flags, int userId);
List queryIntentReceivers(in Intent intent, String resolvedType, int flags, int userId);
ResolveInfo resolveService(in Intent intent, String resolvedType, int flags, int userId);
List queryIntentServices(in Intent intent, String resolvedType, int flags, int userId);
List queryIntentContentProviders(in Intent intent, String resolvedType, int flags, int userId);
VParceledListSlice getInstalledPackages(int flags, int userId);
VParceledListSlice getInstalledApplications(int flags, int userId);
PermissionInfo getPermissionInfo(in String name, int flags);
List queryPermissionsByGroup(in String group, int flags);
PermissionGroupInfo getPermissionGroupInfo(in String name, int flags);
List getAllPermissionGroups(int flags);
ProviderInfo resolveContentProvider(in String name, int flags, int userId);
ApplicationInfo getApplicationInfo(in String packageName, int flags, int userId);
VParceledListSlice queryContentProviders(in String processName, int vuid, int flags);
List querySharedPackages(in String packageName);
String getNameForUid(int uid);
IPackageInstaller getPackageInstaller();
}
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IUserManager.aidl
================================================
package com.lody.virtual.server;
import android.os.ParcelFileDescriptor;
import com.lody.virtual.os.VUserInfo;
import android.graphics.Bitmap;
/**
*
*/
interface IUserManager {
VUserInfo createUser(in String name, int flags);
boolean removeUser(int userHandle);
void setUserName(int userHandle, String name);
void setUserIcon(int userHandle, in Bitmap icon);
Bitmap getUserIcon(int userHandle);
List getUsers(boolean excludeDying);
VUserInfo getUserInfo(int userHandle);
void setGuestEnabled(boolean enable);
boolean isGuestEnabled();
void wipeUser(int userHandle);
int getUserSerialNumber(int userHandle);
int getUserHandle(int userSerialNumber);
}
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IVirtualLocationManager.aidl
================================================
// IVirtualLocationManager.aidl
package com.lody.virtual.server;
import com.lody.virtual.remote.vloc.VCell;
import com.lody.virtual.remote.vloc.VWifi;
import com.lody.virtual.remote.vloc.VLocation;
interface IVirtualLocationManager {
int getMode(int userId, in String pkg);
void setMode(int userId, in String pkg, int mode);
void setCell(in int userId, in String pkg, in VCell cell);
void setAllCell(in int userId, in String pkg, in List cell);
void setNeighboringCell(in int userId, in String pkg, in List cell);
void setGlobalCell(in VCell cell);
void setGlobalAllCell(in List cell);
void setGlobalNeighboringCell(in List cell);
VCell getCell(in int userId, in String pkg);
List getAllCell(in int userId, in String pkg);
List getNeighboringCell(in int userId, in String pkg);
void setLocation(in int userId, in String pkg, in VLocation loc);
VLocation getLocation(in int userId, in String pkg);
void setGlobalLocation(in VLocation loc);
VLocation getGlobalLocation();
}
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/server/IVirtualStorageService.aidl
================================================
// IVirtualStorageService.aidl
package com.lody.virtual.server;
interface IVirtualStorageService {
void setVirtualStorage(in String packageName, in int userId, in String vsPath);
String getVirtualStorage(in String packageName, in int userId);
void setVirtualStorageState(in String packageName, in int userId, in boolean enable);
boolean isVirtualStorageEnable(in String packageName, in int userId);
}
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/server/interfaces/IAppRequestListener.aidl
================================================
// IAppRequestListener.aidl
package com.lody.virtual.server.interfaces;
interface IAppRequestListener {
void onRequestInstall(in String path);
void onRequestUninstall(in String pkg);
}
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/server/interfaces/IIntentFilterObserver.aidl
================================================
// IIntentFilterObserver.aidl
package com.lody.virtual.server.interfaces;
// Declare any non-default types here with import statements
interface IIntentFilterObserver {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
Intent filter(in Intent intent);
void setCallBack(IBinder callBack);
IBinder getCallBack();
}
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/server/interfaces/IPackageObserver.aidl
================================================
// IPackageObserver.aidl
package com.lody.virtual.server.interfaces;
interface IPackageObserver {
void onPackageInstalled(in String packageName);
void onPackageUninstalled(in String packageName);
void onPackageInstalledAsUser(in int userId, in String packageName);
void onPackageUninstalledAsUser(in int userId, in String packageName);
}
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/server/interfaces/IProcessObserver.aidl
================================================
// IProcessObserver.aidl
package com.lody.virtual.server.interfaces;
interface IProcessObserver {
void onProcessCreated(in String pkg, in String processName);
void onProcessDied(in String pkg, in String processName);
}
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/server/interfaces/IServiceFetcher.aidl
================================================
// IServiceFetcher.aidl
package com.lody.virtual.server.interfaces;
interface IServiceFetcher {
IBinder getService(String name);
void addService(String name,in IBinder service);
void removeService(String name);
}
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/server/interfaces/IUiCallback.aidl
================================================
// IUiCallback.aidl
package com.lody.virtual.server.interfaces;
interface IUiCallback {
void onAppOpened(in String packageName, in int userId);
void onOpenFailed(in String packageName, in int userId);
}
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/server/pm/installer/SessionInfo.aidl
================================================
// SessionInfo.aidl
package com.lody.virtual.server.pm.installer;
parcelable SessionInfo;
================================================
FILE: VirtualApp/lib/src/main/aidl/com/lody/virtual/server/pm/installer/SessionParams.aidl
================================================
// SessionParams.aidl
package com.lody.virtual.server.pm.installer;
parcelable SessionParams;
================================================
FILE: VirtualApp/lib/src/main/java/android/app/ActivityOptions.java
================================================
package android.app;
/**
* @author weishu
* @date 2021/2/24.
*/
class ActivityOptions {
}
================================================
FILE: VirtualApp/lib/src/main/java/android/app/ActivityThread.java
================================================
package android.app;
/**
* @author weishu
* @date 2018/8/7.
*/
public class ActivityThread {
public static class ActivityClientRecord {}
}
================================================
FILE: VirtualApp/lib/src/main/java/android/app/ClientTransactionHandler.java
================================================
/*
* Copyright 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.PendingTransactionActions;
import android.app.servertransaction.TransactionExecutor;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.os.IBinder;
import android.os.Parcelable;
import android.util.MergedConfiguration;
import android.view.DisplayAdjustments;
import java.util.List;
import java.util.Map;
import mirror.com.android.internal.content.ReferrerIntent;
/**
* Defines operations that a {@link ClientTransaction} or its items
* can perform on client.
* @hide
*/
public abstract class ClientTransactionHandler {
// Schedule phase related logic and handlers.
/** Prepare and schedule transaction for execution. */
void scheduleTransaction(ClientTransaction transaction) {
}
/**
* Execute transaction immediately without scheduling it. This is used for local requests, so
* it will also recycle the transaction.
*/
public void executeTransaction(ClientTransaction transaction) {
}
/**
* Get the {@link TransactionExecutor} that will be performing lifecycle transitions and
* callbacks for activities.
*/
abstract TransactionExecutor getTransactionExecutor();
abstract void sendMessage(int what, Object obj);
// Prepare phase related logic and handlers. Methods that inform about about pending changes or
// do other internal bookkeeping.
/** Set pending config in case it will be updated by other transaction item. */
public abstract void updatePendingConfiguration(Configuration config);
/** Set current process state. */
public abstract void updateProcessState(int processState, boolean fromIpc);
// Execute phase related logic and handlers. Methods here execute actual lifecycle transactions
// and deliver callbacks.
/** Destroy the activity. */
public abstract void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
boolean getNonConfigInstance, String reason);
/** Pause the activity. */
public abstract void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
int configChanges, PendingTransactionActions pendingActions, String reason);
// Android 12
/** Destroy the activity. */
public abstract void handleDestroyActivity(ActivityThread.ActivityClientRecord r, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason);
/** Pause the activity. */
public abstract void handlePauseActivity(ActivityThread.ActivityClientRecord r, boolean finished, boolean userLeaving,
int configChanges, PendingTransactionActions pendingActions, String reason);
/**
* Resume the activity.
* @param token Target activity token.
* @param finalStateRequest Flag indicating if this call is handling final lifecycle state
* request for a transaction.
* @param isForward Flag indicating if next transition is forward.
* @param reason Reason for performing this operation.
*/
public abstract void handleResumeActivity(IBinder token, boolean finalStateRequest,
boolean isForward, String reason);
// Android 12
public abstract void handleResumeActivity(ActivityThread.ActivityClientRecord record, boolean finalStateRequest,
boolean isForward, String reason);
/**
* Stop the activity.
* @param token Target activity token.
* @param show Flag indicating whether activity is still shown.
* @param configChanges Activity configuration changes.
* @param pendingActions Pending actions to be used on this or later stages of activity
* transaction.
* @param finalStateRequest Flag indicating if this call is handling final lifecycle state
* request for a transaction.
* @param reason Reason for performing this operation.
*/
public abstract void handleStopActivity(IBinder token, boolean show, int configChanges,
PendingTransactionActions pendingActions, boolean finalStateRequest, String reason);
// Android 11
public abstract void handleStopActivity(IBinder token, int configChanges,
PendingTransactionActions pendingActions, boolean finalStateRequest, String reason);
// Android 12
public abstract void handleStopActivity(ActivityThread.ActivityClientRecord r, int configChanges,
PendingTransactionActions pendingActions, boolean finalStateRequest, String reason);
/** Report that activity was stopped to server. */
public abstract void reportStop(PendingTransactionActions pendingActions);
/** Restart the activity after it was stopped. */
public abstract void performRestartActivity(IBinder token, boolean start);
/** Restart the activity after it was stopped. */
public abstract void performRestartActivity(ActivityThread.ActivityClientRecord r, boolean start);
/** Deliver activity (override) configuration change. */
public abstract void handleActivityConfigurationChanged(IBinder activityToken,
Configuration overrideConfig, int displayId);
public abstract void handleActivityConfigurationChanged(ActivityThread.ActivityClientRecord r,
Configuration overrideConfig, int displayId);
/** Deliver result from another activity. */
public abstract void handleSendResult(IBinder token, List results, String reason);
/** Deliver result from another activity. */
public abstract void handleSendResult(
ActivityThread.ActivityClientRecord r, List results, String reason);
/** Deliver multi-window mode change notification. */
public abstract void handleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode,
Configuration overrideConfig);
/** Deliver new intent. */
public abstract void handleNewIntent(IBinder token, List intents,
boolean andPause);
public abstract void handleNewIntent(
ActivityThread.ActivityClientRecord r, 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. */
public abstract Activity handleLaunchActivity(ActivityThread.ActivityClientRecord r,
PendingTransactionActions pendingActions, Intent customIntent);
/** 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.
*/
public abstract ActivityThread.ActivityClientRecord getActivityClient(IBinder token);
/**
* Prepare activity relaunch to update internal bookkeeping. This is used to track multiple
* relaunch and config update requests.
* @param token Activity token.
* @param pendingResults Activity results to be delivered.
* @param pendingNewIntents New intent messages to be delivered.
* @param configChanges Mask of configuration changes that have occurred.
* @param config New configuration applied to the activity.
* @param preserveWindow Whether the activity should try to reuse the window it created,
* including the decor view after the relaunch.
* @return An initialized instance of {@link ActivityThread.ActivityClientRecord} to use during
* relaunch, or {@code null} if relaunch cancelled.
*/
public abstract ActivityThread.ActivityClientRecord prepareRelaunchActivity(IBinder token,
List pendingResults, List pendingNewIntents,
int configChanges, MergedConfiguration config, boolean preserveWindow);
/**
* Perform activity relaunch.
* @param r Activity client record prepared for relaunch.
* @param pendingActions Pending actions to be used on later stages of activity transaction.
* */
public abstract void handleRelaunchActivity(ActivityThread.ActivityClientRecord r,
PendingTransactionActions pendingActions);
/**
* Report that relaunch request was handled.
* @param token Target activity token.
* @param pendingActions Pending actions initialized on earlier stages of activity transaction.
* Used to check if we should report relaunch to WM.
* */
public abstract void reportRelaunch(IBinder token, PendingTransactionActions pendingActions);
public abstract Map getActivitiesToBeDestroyed();
// Android 10
public abstract Activity getActivity(IBinder token);
public abstract void updatePendingActivityConfiguration(IBinder arg1, Configuration arg2);
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);
/** Deliver new intent. */
public abstract void handleNewIntent(IBinder token, List intents);
}
================================================
FILE: VirtualApp/lib/src/main/java/android/app/LoadedApk.java
================================================
package android.app;
/**
* @author weishu
* @date 2018/8/7.
*/
public class LoadedApk {
}
================================================
FILE: VirtualApp/lib/src/main/java/android/app/TransactionHandlerProxy.java
================================================
package android.app;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.servertransaction.PendingTransactionActions;
import android.app.servertransaction.TransactionExecutor;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
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;
import com.lody.virtual.client.ipc.VActivityManager;
import com.lody.virtual.helper.utils.ComponentUtils;
import com.lody.virtual.remote.InstalledAppInfo;
import com.lody.virtual.remote.StubActivityRecord;
import java.util.List;
import java.util.Map;
import mirror.android.app.ActivityManagerNative;
import mirror.android.app.IActivityManager;
import mirror.com.android.internal.content.ReferrerIntent;
/**
* @author weishu
* @date 2018/8/7.
*/
public class TransactionHandlerProxy extends ClientTransactionHandler {
private static final String TAG = "TransactionHandlerProxy";
private ClientTransactionHandler originalHandler;
public TransactionHandlerProxy(ClientTransactionHandler originalHandler) {
this.originalHandler = originalHandler;
}
@Override
TransactionExecutor getTransactionExecutor() {
return originalHandler.getTransactionExecutor();
}
@Override
void sendMessage(int what, Object obj) {
originalHandler.sendMessage(what, obj);
}
@Override
public void updatePendingConfiguration(Configuration config) {
originalHandler.updatePendingConfiguration(config);
}
@Override
public void updateProcessState(int processState, boolean fromIpc) {
originalHandler.updateProcessState(processState, fromIpc);
}
@Override
public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) {
originalHandler.handleDestroyActivity(token, finishing, configChanges, getNonConfigInstance, reason);
}
@Override
public void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving, int configChanges, PendingTransactionActions pendingActions, String reason) {
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);
}
@Override
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);
}
@Override
public void handleNewIntent(IBinder token, List intents, boolean andPause) {
originalHandler.handleNewIntent(token, intents, andPause);
}
@Override
public void handlePictureInPictureModeChanged(IBinder token, boolean isInPipMode, Configuration overrideConfig) {
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);
}
@Override
public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent) {
Intent stubIntent = mirror.android.app.ActivityThread.ActivityClientRecord.intent.get(r);
StubActivityRecord saveInstance = new StubActivityRecord(stubIntent);
if (saveInstance.intent == null) {
Log.i(TAG, "save instance intent is null, return");
return null;
}
Intent intent = saveInstance.intent;
ComponentName caller = saveInstance.caller;
IBinder token = mirror.android.app.ActivityThread.ActivityClientRecord.token.get(r);
ActivityInfo info = saveInstance.info;
if (VClientImpl.get().getToken() == null) {
InstalledAppInfo installedAppInfo = VirtualCore.get().getInstalledAppInfo(info.packageName, 0);
if (installedAppInfo == null) {
Log.i(TAG, "install app info is null, return");
return null;
}
VActivityManager.get().processRestarted(info.packageName, info.processName, saveInstance.userId);
// getH().sendMessageAtFrontOfQueue(Message.obtain(msg));
Log.i(TAG, "restart process, return");
return handleLaunchActivity(r, pendingActions, customIntent);
}
if (!VClientImpl.get().isBound()) {
VClientImpl.get().bindApplicationForActivity(info.packageName, info.processName, intent);
// getH().sendMessageAtFrontOfQueue(Message.obtain(msg));
Log.i(TAG, "rebound application, return");
return handleLaunchActivity(r, pendingActions, customIntent);
}
int taskId = IActivityManager.getTaskForActivity.call(
ActivityManagerNative.getDefault.call(),
token,
false
);
Object packageInfo = mirror.android.app.ActivityThread.ActivityClientRecord.packageInfo.get(r);
mirror.android.app.ActivityThread.ActivityClientRecord.packageInfo.set(r, null);
VActivityManager.get().onActivityCreate(ComponentUtils.toComponentName(info), caller, token, info, intent, ComponentUtils.getTaskAffinity(info), taskId, info.launchMode, info.flags);
ClassLoader appClassLoader = VClientImpl.get().getClassLoader(info.applicationInfo);
intent.setExtrasClassLoader(appClassLoader);
mirror.android.app.ActivityThread.ActivityClientRecord.intent.set(r, intent);
mirror.android.app.ActivityThread.ActivityClientRecord.activityInfo.set(r, info);
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);
}
@Override
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);
return originalHandler.getActivityClient(token);
}
@Override
public ActivityClientRecord prepareRelaunchActivity(IBinder token, List pendingResults, List pendingNewIntents, int configChanges, MergedConfiguration config, boolean preserveWindow) {
return originalHandler.prepareRelaunchActivity(token, pendingResults, pendingNewIntents, configChanges, config, preserveWindow);
}
@Override
public void handleRelaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions) {
originalHandler.handleRelaunchActivity(r, pendingActions);
}
@Override
public void reportRelaunch(IBinder token, PendingTransactionActions pendingActions) {
originalHandler.reportRelaunch(token, pendingActions);
}
@Override
public Map getActivitiesToBeDestroyed() {
return originalHandler.getActivitiesToBeDestroyed();
}
@Override
public Activity getActivity(IBinder token) {
return originalHandler.getActivity(token);
}
@Override
public void updatePendingActivityConfiguration(IBinder arg1, Configuration arg2) {
originalHandler.updatePendingActivityConfiguration(arg1, arg2);
}
@Override
public void handleTopResumedActivityChanged(IBinder arg1, boolean arg2, String arg3) {
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);
}
@Override
public void handleNewIntent(IBinder token, List intents) {
originalHandler.handleNewIntent(token, intents);
}
@Override
public void handleNewIntent(ActivityClientRecord r, List intents) {
originalHandler.handleNewIntent(r, intents);
}
}
================================================
FILE: VirtualApp/lib/src/main/java/android/app/servertransaction/ClientTransaction.java
================================================
/*
* Copyright 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.servertransaction;
/**
* A container that holds a sequence of messages, which may be sent to a client.
* This includes a list of callbacks and a final lifecycle state.
*
* @see com.android.server.am.ClientLifecycleManager
* @see ClientTransactionItem
* @see ActivityLifecycleItem
* @hide
*/
public class ClientTransaction {
}
================================================
FILE: VirtualApp/lib/src/main/java/android/app/servertransaction/PendingTransactionActions.java
================================================
/*
* Copyright 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.servertransaction;
/**
* Container that has data pending to be used at later stages of
* {@link ClientTransaction}.
* An instance of this class is passed to each individual transaction item, so it can use some
* information from previous steps or add some for the following steps.
*
* @hide
*/
public class PendingTransactionActions {
}
================================================
FILE: VirtualApp/lib/src/main/java/android/app/servertransaction/TransactionExecutor.java
================================================
/*
* Copyright 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.servertransaction;
/**
* Class that manages transaction execution in the correct order.
* @hide
*/
public class TransactionExecutor {
}
================================================
FILE: VirtualApp/lib/src/main/java/android/content/SyncStatusInfo.java
================================================
package android.content;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import java.util.ArrayList;
public class SyncStatusInfo implements Parcelable {
static final int VERSION = 2;
public final int authorityId;
public long totalElapsedTime;
public int numSyncs;
public int numSourcePoll;
public int numSourceServer;
public int numSourceLocal;
public int numSourceUser;
public int numSourcePeriodic;
public long lastSuccessTime;
public int lastSuccessSource;
public long lastFailureTime;
public int lastFailureSource;
public String lastFailureMesg;
public long initialFailureTime;
public boolean pending;
public boolean initialize;
// Warning: It is up to the external caller to ensure there are
// no race conditions when accessing this list
private ArrayList periodicSyncTimes;
private static final String TAG = "Sync";
public SyncStatusInfo(int authorityId) {
this.authorityId = authorityId;
}
public int getLastFailureMesgAsInt(int def) {
return 0;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(VERSION);
parcel.writeInt(authorityId);
parcel.writeLong(totalElapsedTime);
parcel.writeInt(numSyncs);
parcel.writeInt(numSourcePoll);
parcel.writeInt(numSourceServer);
parcel.writeInt(numSourceLocal);
parcel.writeInt(numSourceUser);
parcel.writeLong(lastSuccessTime);
parcel.writeInt(lastSuccessSource);
parcel.writeLong(lastFailureTime);
parcel.writeInt(lastFailureSource);
parcel.writeString(lastFailureMesg);
parcel.writeLong(initialFailureTime);
parcel.writeInt(pending ? 1 : 0);
parcel.writeInt(initialize ? 1 : 0);
if (periodicSyncTimes != null) {
parcel.writeInt(periodicSyncTimes.size());
for (long periodicSyncTime : periodicSyncTimes) {
parcel.writeLong(periodicSyncTime);
}
} else {
parcel.writeInt(-1);
}
}
public SyncStatusInfo(Parcel parcel) {
int version = parcel.readInt();
if (version != VERSION && version != 1) {
Log.w("SyncStatusInfo", "Unknown version: " + version);
}
authorityId = parcel.readInt();
totalElapsedTime = parcel.readLong();
numSyncs = parcel.readInt();
numSourcePoll = parcel.readInt();
numSourceServer = parcel.readInt();
numSourceLocal = parcel.readInt();
numSourceUser = parcel.readInt();
lastSuccessTime = parcel.readLong();
lastSuccessSource = parcel.readInt();
lastFailureTime = parcel.readLong();
lastFailureSource = parcel.readInt();
lastFailureMesg = parcel.readString();
initialFailureTime = parcel.readLong();
pending = parcel.readInt() != 0;
initialize = parcel.readInt() != 0;
if (version == 1) {
periodicSyncTimes = null;
} else {
int N = parcel.readInt();
if (N < 0) {
periodicSyncTimes = null;
} else {
periodicSyncTimes = new ArrayList();
for (int i=0; i(other.periodicSyncTimes);
}
}
public void setPeriodicSyncTime(int index, long when) {
// The list is initialized lazily when scheduling occurs so we need to make sure
// we initialize elements < index to zero (zero is ignore for scheduling purposes)
ensurePeriodicSyncTimeSize(index);
periodicSyncTimes.set(index, when);
}
public long getPeriodicSyncTime(int index) {
if (periodicSyncTimes != null && index < periodicSyncTimes.size()) {
return periodicSyncTimes.get(index);
} else {
return 0;
}
}
public void removePeriodicSyncTime(int index) {
if (periodicSyncTimes != null && index < periodicSyncTimes.size()) {
periodicSyncTimes.remove(index);
}
}
public static final Creator CREATOR = new Creator() {
public SyncStatusInfo createFromParcel(Parcel in) {
return new SyncStatusInfo(in);
}
public SyncStatusInfo[] newArray(int size) {
return new SyncStatusInfo[size];
}
};
private void ensurePeriodicSyncTimeSize(int index) {
if (periodicSyncTimes == null) {
periodicSyncTimes = new ArrayList<>(0);
}
final int requiredSize = index + 1;
if (periodicSyncTimes.size() < requiredSize) {
for (int i = periodicSyncTimes.size(); i < requiredSize; i++) {
periodicSyncTimes.add((long) 0);
}
}
}
}
================================================
FILE: VirtualApp/lib/src/main/java/android/content/pm/PackageParser.java
================================================
package android.content.pm;
import android.content.ComponentName;
import android.content.IntentFilter;
import android.os.Bundle;
import java.util.ArrayList;
/**
* @author Lody
*/
public class PackageParser {
public static final int PARSE_IS_SYSTEM = 1;
public static class IntentInfo extends IntentFilter {
public boolean hasDefault;
public int labelRes;
public CharSequence nonLocalizedLabel;
public int icon;
public int logo;
public int banner;
}
public static class Component {
public Package owner;
public ArrayList intents;
public String className;
public Bundle metaData;
public ComponentName getComponentName() {
return null;
}
}
public final static class Activity extends Component {
public ActivityInfo info;
}
public class Package {
public final ArrayList activities = new ArrayList(0);
public final ArrayList receivers = new ArrayList(0);
public final ArrayList providers = new ArrayList(0);
public final ArrayList services = new ArrayList(0);
public final ArrayList instrumentation = new ArrayList(0);
public final ArrayList permissions = new ArrayList(0);
public final ArrayList permissionGroups = new ArrayList(0);
public final ArrayList requestedPermissions = new ArrayList();
public Signature[] mSignatures;
public Bundle mAppMetaData;
public Object mExtras;
public String packageName;
public int mPreferredOrder;
public String mSharedUserId;
public ArrayList usesLibraries;
public int mVersionCode;
public ApplicationInfo applicationInfo;
public String mVersionName;
// Applications hardware preferences
public ArrayList configPreferences = null;
// 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 {
public ServiceInfo info;
}
public final class Provider extends Component {
public ProviderInfo info;
}
public final class Instrumentation extends Component {
public InstrumentationInfo info;
}
public final class Permission extends Component {
public PermissionInfo info;
}
public final class PermissionGroup extends Component {
public PermissionGroupInfo info;
}
public class ActivityIntentInfo extends IntentInfo {
public Activity activity;
}
public class ServiceIntentInfo extends IntentInfo {
public Service service;
}
public class ProviderIntentInfo extends IntentInfo {
public Provider provider;
}
}
================================================
FILE: VirtualApp/lib/src/main/java/android/content/res/CompatibilityInfo.java
================================================
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.content.res;
/**
* CompatibilityInfo class keeps the information about compatibility mode that the application is
* running under.
*
* {@hide}
*/
public class CompatibilityInfo {
}
================================================
FILE: VirtualApp/lib/src/main/java/android/location/LocationRequest.java
================================================
package android.location;
import android.os.Parcel;
import android.os.Parcelable;
public final class LocationRequest implements Parcelable {
public String getProvider() {
return null;
}
public static final Creator CREATOR = new Creator() {
@Override
public LocationRequest createFromParcel(Parcel in) {
return null;
}
@Override
public LocationRequest[] newArray(int size) {
return null;
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
}
}
================================================
FILE: VirtualApp/lib/src/main/java/android/util/MergedConfiguration.java
================================================
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package android.util;
/**
* Container that holds global and override config and their merge product.
* Merged configuration updates automatically whenever global or override configs are updated via
* setters.
*
* {@hide}
*/
public class MergedConfiguration {
}
================================================
FILE: VirtualApp/lib/src/main/java/android/view/DisplayAdjustments.java
================================================
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 {}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/Build.java
================================================
package com.lody.virtual;
/**
*
* Version info of VirtualApp project.
*
* @author Lody
*
*/
public class Build {
public static final String VERSION_NAME = "Build-823-01";
public static final int VERSION_CODE = 8230001;
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/DelegateApplication64Bit.java
================================================
package com.lody.virtual;
import android.annotation.TargetApi;
import android.app.Application;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.os.Build;
import android.util.Log;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author Lody
*
*
* Copy the file to your Project.
*/
@TargetApi(Build.VERSION_CODES.M)
public abstract class DelegateApplication64Bit extends Application {
private Application mTarget;
protected abstract String get32BitPackageName();
private static Field findField(Object instance, String name) throws NoSuchFieldException {
for (Class> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
try {
Field field = clazz.getDeclaredField(name);
if (!field.isAccessible()) {
field.setAccessible(true);
}
return field;
} catch (NoSuchFieldException e) {
// ignore and search next
}
}
throw new NoSuchFieldException("Field " + name + " not found in " + instance.getClass());
}
private static Method findMethod(Object instance, String name, Class>... parameterTypes)
throws NoSuchMethodException {
for (Class> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
try {
Method method = clazz.getDeclaredMethod(name, parameterTypes);
if (!method.isAccessible()) {
method.setAccessible(true);
}
return method;
} catch (NoSuchMethodException e) {
// ignore and search next
}
}
throw new NoSuchMethodException("Method " + name + " with parameters " +
Arrays.asList(parameterTypes) + " not found in " + instance.getClass());
}
private static void expandFieldArray(Object instance, String fieldName,
Object[] extraElements) throws NoSuchFieldException, IllegalArgumentException,
IllegalAccessException {
Field jlrField = findField(instance, fieldName);
Object[] original = (Object[]) jlrField.get(instance);
Object[] combined = (Object[]) Array.newInstance(
original.getClass().getComponentType(), original.length + extraElements.length);
System.arraycopy(original, 0, combined, 0, original.length);
System.arraycopy(extraElements, 0, combined, original.length, extraElements.length);
jlrField.set(instance, combined);
}
private static void expandFieldList(Object instance, String fieldName, Object[] extraElements) throws NoSuchFieldException, IllegalAccessException {
Field field = findField(instance, fieldName);
Object[] original = ((List) field.get(instance)).toArray();
Object[] combined = (Object[]) Array.newInstance(original.getClass().getComponentType(), original.length + 1);
System.arraycopy(original, 0, combined, 0, original.length);
System.arraycopy(extraElements, 0, combined, original.length, 1);
field.set(instance, Arrays.asList(combined));
}
private static Object[] makeDexElements(
Object dexPathList, ArrayList files,
ArrayList suppressedExceptions)
throws IllegalAccessException, InvocationTargetException,
NoSuchMethodException {
Method makeDexElements;
if (Build.VERSION.SDK_INT >= 23) {
makeDexElements = findMethod(dexPathList, "makePathElements", List.class, File.class, List.class);
} else {
makeDexElements = findMethod(dexPathList, "makeDexElements", ArrayList.class, File.class, ArrayList.class);
}
return (Object[]) makeDexElements.invoke(dexPathList, files, null,
suppressedExceptions);
}
protected void attachBaseContext(Context context) {
super.attachBaseContext(context);
try {
ApplicationInfo ai = getPackageManager().getApplicationInfo(get32BitPackageName(), 0);
ClassLoader classLoader = getClassLoader();
Object dexPathList = findField(classLoader, "pathList").get(classLoader);
ArrayList suppressedExceptions = new ArrayList<>();
ArrayList dexFiles = new ArrayList<>();
dexFiles.add(new File(ai.publicSourceDir));
ArrayList nativeLibs = new ArrayList<>();
nativeLibs.add(new File(ai.nativeLibraryDir));
if (Build.VERSION.SDK_INT > 25) {
expandFieldList(dexPathList, "nativeLibraryDirectories", new File[]{new File(ai.nativeLibraryDir)});
expandFieldArray(dexPathList, "nativeLibraryPathElements",
(Object[]) findMethod(dexPathList, "makePathElements", List.class).invoke(dexPathList, nativeLibs));
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
expandFieldList(dexPathList, "nativeLibraryDirectories", new File[]{new File(ai.nativeLibraryDir)});
expandFieldArray(dexPathList, "nativeLibraryPathElements", makeDexElements(dexPathList, nativeLibs, suppressedExceptions));
} else {
expandFieldArray(dexPathList, "nativeLibraryDirectories", new File[]{new File(ai.nativeLibraryDir)});
}
expandFieldArray(dexPathList, "dexElements", makeDexElements(dexPathList, dexFiles, suppressedExceptions));
if (suppressedExceptions.size() > 0) {
for (IOException e : suppressedExceptions) {
Log.w(getClass().getSimpleName(), "Exception in makeDexElement", e);
}
Field suppressedExceptionsField =
findField(classLoader, "dexElementsSuppressedExceptions");
IOException[] dexElementsSuppressedExceptions =
(IOException[]) suppressedExceptionsField.get(classLoader);
if (dexElementsSuppressedExceptions == null) {
dexElementsSuppressedExceptions =
suppressedExceptions.toArray(
new IOException[suppressedExceptions.size()]);
} else {
IOException[] combined =
new IOException[suppressedExceptions.size() +
dexElementsSuppressedExceptions.length];
suppressedExceptions.toArray(combined);
System.arraycopy(dexElementsSuppressedExceptions, 0, combined,
suppressedExceptions.size(), dexElementsSuppressedExceptions.length);
dexElementsSuppressedExceptions = combined;
}
suppressedExceptionsField.set(classLoader, dexElementsSuppressedExceptions);
}
mTarget = (Application) classLoader.loadClass(ai.className).newInstance();
} catch (Throwable e) {
e.printStackTrace();
}
}
public void onConfigurationChanged(Configuration configuration) {
super.onConfigurationChanged(configuration);
if (mTarget != null) {
mTarget.onConfigurationChanged(configuration);
}
}
public void onCreate() {
super.onCreate();
if (mTarget != null) {
mTarget.onCreate();
}
}
public void onLowMemory() {
super.onLowMemory();
if (mTarget != null) {
mTarget.onLowMemory();
}
}
public void onTerminate() {
super.onTerminate();
if (mTarget != null) {
mTarget.onTerminate();
}
}
public void onTrimMemory(int i) {
super.onTrimMemory(i);
if (mTarget != null) {
mTarget.onTrimMemory(i);
}
}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/GmsSupport.java
================================================
package com.lody.virtual;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import com.lody.virtual.client.core.InstallStrategy;
import com.lody.virtual.client.core.VirtualCore;
import java.util.Arrays;
import java.util.List;
/**
* @author Lody
*/
public class GmsSupport {
public static final List GOOGLE_APP = Arrays.asList(
"com.android.vending",
"com.google.android.play.games",
"com.google.android.wearable.app",
"com.google.android.wearable.app.cn"
);
public static final List GOOGLE_SERVICE = Arrays.asList(
"com.google.android.gsf",
"com.google.android.gms",
"com.google.android.gsf.login",
"com.google.android.backuptransport",
"com.google.android.backup",
"com.google.android.configupdater",
"com.google.android.syncadapters.contacts",
"com.google.android.feedback",
"com.google.android.onetimeinitializer",
"com.google.android.partnersetup",
"com.google.android.setupwizard",
"com.google.android.syncadapters.calendar"
);
public static boolean isGmsFamilyPackage(String packageName) {
return packageName.equals("com.android.vending")
|| packageName.equals("com.google.android.gms");
}
public static boolean isGoogleFrameworkInstalled() {
return VirtualCore.get().isAppInstalled("com.google.android.gms");
}
public static boolean isOutsideGoogleFrameworkExist() {
return VirtualCore.get().isOutsideInstalled("com.google.android.gms");
}
private static void installPackages(List list, int userId) {
VirtualCore core = VirtualCore.get();
for (String packageName : list) {
if (core.isAppInstalledAsUser(userId, packageName)) {
continue;
}
ApplicationInfo info = null;
try {
info = VirtualCore.get().getUnHookPackageManager().getApplicationInfo(packageName, 0);
} catch (PackageManager.NameNotFoundException e) {
// Ignore
}
if (info == null || info.sourceDir == null) {
continue;
}
if (userId == 0) {
core.installPackage(info.sourceDir, InstallStrategy.DEPEND_SYSTEM_IF_EXIST);
} else {
core.installPackageAsUser(userId, packageName);
}
}
}
public static void installGApps(int userId) {
installPackages(GOOGLE_SERVICE, userId);
installPackages(GOOGLE_APP, userId);
}
public static void installGoogleService(int userId) {
installPackages(GOOGLE_SERVICE, userId);
}
public static void installGoogleApp(int userId) {
installPackages(GOOGLE_APP, userId);
}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/NativeEngine.java
================================================
package com.lody.virtual.client;
import android.os.Binder;
import android.os.Build;
import android.os.Process;
import com.lody.virtual.client.core.VirtualCore;
import com.lody.virtual.client.env.VirtualRuntime;
import com.lody.virtual.client.ipc.VActivityManager;
import com.lody.virtual.client.natives.NativeMethods;
import com.lody.virtual.helper.compat.BuildCompat;
import com.lody.virtual.helper.utils.DeviceUtil;
import com.lody.virtual.helper.utils.VLog;
import com.lody.virtual.os.VUserHandle;
import com.lody.virtual.remote.InstalledAppInfo;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* VirtualApp Native Project
*/
public class NativeEngine {
private static final String TAG = NativeEngine.class.getSimpleName();
private static final String VESCAPE = "/6decacfa7aad11e8a718985aebe4663a";
private static Map sDexOverrideMap;
private static boolean sFlag = false;
private static final String LIB_NAME = "va++";
static {
try {
System.loadLibrary(LIB_NAME);
} catch (Throwable e) {
VLog.e(TAG, VLog.getStackTraceString(e));
}
}
static {
NativeMethods.init();
}
public static void startDexOverride() {
List installedAppInfos = VirtualCore.get().getInstalledApps(0);
sDexOverrideMap = new HashMap<>(installedAppInfos.size());
for (InstalledAppInfo info : installedAppInfos) {
try {
sDexOverrideMap.put(new File(info.apkPath).getCanonicalPath(), info);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static String getRedirectedPath(String redirectPath) {
try {
return nativeGetRedirectedPath(redirectPath);
} catch (Throwable e) {
VLog.e(TAG, VLog.getStackTraceString(e));
}
return redirectPath;
}
public static String resverseRedirectedPath(String origPath) {
try {
return nativeReverseRedirectedPath(origPath);
} catch (Throwable e) {
VLog.e(TAG, VLog.getStackTraceString(e));
}
return origPath;
}
public static void redirectDirectory(String origPath, String newPath) {
if (!origPath.endsWith("/")) {
origPath = origPath + "/";
}
if (!newPath.endsWith("/")) {
newPath = newPath + "/";
}
try {
nativeIORedirect(origPath, newPath);
} catch (Throwable e) {
VLog.e(TAG, VLog.getStackTraceString(e));
}
}
public static String getEscapePath(String path) {
if (path == null) {
return null;
}
File file = new File(path);
if (file.exists()) {
return file.getAbsolutePath();
}
return new File(VESCAPE, path).getAbsolutePath();
}
public static void redirectFile(String origPath, String newPath) {
if (origPath.endsWith("/")) {
origPath = origPath.substring(0, origPath.length() - 1);
}
if (newPath.endsWith("/")) {
newPath = newPath.substring(0, newPath.length() - 1);
}
try {
nativeIORedirect(origPath, newPath);
} catch (Throwable e) {
VLog.e(TAG, VLog.getStackTraceString(e));
}
}
public static void whitelist(String path, boolean directory) {
if (directory && !path.endsWith("/")) {
path = path + "/";
} else if (!directory && path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
try {
nativeIOWhitelist(path);
} catch (Throwable e) {
VLog.e(TAG, VLog.getStackTraceString(e));
}
}
public static void forbid(String path) {
if (!path.endsWith("/")) {
path = path + "/";
}
try {
nativeIOForbid(path);
} catch (Throwable e) {
VLog.e(TAG, VLog.getStackTraceString(e));
}
}
public static void enableIORedirect() {
try {
String soPath = VirtualCore.get().getContext().getApplicationInfo().nativeLibraryDir + File.separator + "lib" + LIB_NAME + ".so";
if (!new File(soPath).exists()) {
throw new RuntimeException("io redirect failed.");
}
redirectDirectory(VESCAPE, "/");
nativeEnableIORedirect(soPath, Build.VERSION.SDK_INT, BuildCompat.getPreviewSDKInt());
} catch (Throwable e) {
VLog.e(TAG, VLog.getStackTraceString(e));
}
}
static void launchEngine() {
if (sFlag) {
return;
}
Method[] methods = {NativeMethods.gOpenDexFileNative, NativeMethods.gCameraNativeSetup, NativeMethods.gAudioRecordNativeCheckPermission};
try {
nativeLaunchEngine(methods, VirtualCore.get().getHostPkg(), VirtualRuntime.isArt(), Build.VERSION.SDK_INT, NativeMethods.gCameraMethodType);
} catch (Throwable e) {
VLog.e(TAG, VLog.getStackTraceString(e));
}
sFlag = true;
}
public static void onKillProcess(int pid, int signal) {
VLog.e(TAG, "killProcess: pid = %d, signal = %d.", pid, signal);
if (pid == android.os.Process.myPid()) {
VLog.e(TAG, VLog.getStackTraceString(new Throwable()));
}
}
public static int onGetCallingUid(int originUid) {
int callingPid = Binder.getCallingPid();
if (callingPid == Process.myPid()) {
return VClientImpl.get().getBaseVUid();
}
if (callingPid == VirtualCore.get().getSystemPid()) {
return Process.SYSTEM_UID;
}
int vuid = VActivityManager.get().getUidByPid(callingPid);
if (vuid != -1) {
return VUserHandle.getAppId(vuid);
}
VLog.w(TAG, String.format("Unknown uid: %s", callingPid));
return VClientImpl.get().getBaseVUid();
}
public static void onOpenDexFileNative(String[] params) {
String dexOrJarPath = params[0];
String outputPath = params[1];
VLog.d(TAG, "DexOrJarPath = %s, OutputPath = %s.", dexOrJarPath, outputPath);
try {
String canonical = new File(dexOrJarPath).getCanonicalPath();
InstalledAppInfo info = sDexOverrideMap.get(canonical);
if (info != null && !info.dependSystem || info != null && DeviceUtil.isMeizuBelowN() && params[1] == null) {
outputPath = info.getOdexFile().getPath();
params[1] = outputPath;
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static native void nativeLaunchEngine(Object[] method, String hostPackageName, boolean isArt, int apiLevel, int cameraMethodType);
private static native void nativeMark();
private static native String nativeReverseRedirectedPath(String redirectedPath);
private static native String nativeGetRedirectedPath(String orgPath);
private static native void nativeIORedirect(String origPath, String newPath);
private static native void nativeIOWhitelist(String path);
private static native void nativeIOForbid(String path);
private static native void nativeEnableIORedirect(String selfSoPath, int apiLevel, int previewApiLevel);
public static native void disableJit(int apiLevel);
public static int onGetUid(int uid) {
return VClientImpl.get().getBaseVUid();
}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/VClientImpl.java
================================================
package com.lody.virtual.client;
import android.annotation.SuppressLint;
import android.app.Application;
import android.app.Instrumentation;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.os.Binder;
import android.os.Build;
import android.os.ConditionVariable;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.StrictMode;
import android.system.ErrnoException;
import android.system.Os;
import com.lody.virtual.client.core.CrashHandler;
import com.lody.virtual.client.core.InvocationStubManager;
import com.lody.virtual.client.core.VirtualCore;
import com.lody.virtual.client.env.SpecialComponentList;
import com.lody.virtual.client.env.VirtualRuntime;
import com.lody.virtual.client.fixer.ContextFixer;
import com.lody.virtual.client.hook.delegate.AppInstrumentation;
import com.lody.virtual.client.hook.providers.ProviderHook;
import com.lody.virtual.client.hook.proxies.am.HCallbackStub;
import com.lody.virtual.client.hook.secondary.ProxyServiceFactory;
import com.lody.virtual.client.ipc.VActivityManager;
import com.lody.virtual.client.ipc.VDeviceManager;
import com.lody.virtual.client.ipc.VPackageManager;
import com.lody.virtual.client.ipc.VirtualStorageManager;
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;
import com.lody.virtual.remote.InstalledAppInfo;
import com.lody.virtual.remote.PendingResultData;
import com.lody.virtual.remote.VDeviceInfo;
import com.lody.virtual.server.interfaces.IUiCallback;
import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
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;
import mirror.android.app.ContextImpl;
import mirror.android.app.IActivityManager;
import mirror.android.app.LoadedApk;
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;
import mirror.com.android.internal.content.ReferrerIntent;
import mirror.dalvik.system.VMRuntime;
import mirror.java.lang.ThreadGroupN;
import static com.lody.virtual.os.VUserHandle.getUserId;
/**
* @author Lody
*/
public final class VClientImpl extends IVClient.Stub {
private static final int NEW_INTENT = 11;
private static final int RECEIVER = 12;
private static final String TAG = VClientImpl.class.getSimpleName();
@SuppressLint("StaticFieldLeak")
private static final VClientImpl gClient = new VClientImpl();
private final H mH = new H();
private ConditionVariable mTempLock;
private Instrumentation mInstrumentation = AppInstrumentation.getDefault();
private IBinder token;
private int vuid;
private VDeviceInfo deviceInfo;
private AppBindData mBoundApplication;
private Application mInitialApplication;
private CrashHandler crashHandler;
private IUiCallback mUiCallback;
public static VClientImpl get() {
return gClient;
}
public boolean isBound() {
return mBoundApplication != null;
}
public VDeviceInfo getDeviceInfo() {
if (deviceInfo == null) {
synchronized (this) {
if (deviceInfo == null) {
deviceInfo = VDeviceManager.get().getDeviceInfo(getUserId(vuid));
}
}
}
return deviceInfo;
}
public Application getCurrentApplication() {
return mInitialApplication;
}
public String getCurrentPackage() {
return mBoundApplication != null ?
mBoundApplication.appInfo.packageName : VPackageManager.get().getNameForUid(getVUid());
}
public ApplicationInfo getCurrentApplicationInfo() {
return mBoundApplication != null ? mBoundApplication.appInfo : null;
}
public CrashHandler getCrashHandler() {
return crashHandler;
}
public void setCrashHandler(CrashHandler crashHandler) {
this.crashHandler = crashHandler;
}
public int getVUid() {
return vuid;
}
public int getBaseVUid() {
return VUserHandle.getAppId(vuid);
}
public ClassLoader getClassLoader(ApplicationInfo appInfo) {
Context context = createPackageContext(appInfo.packageName);
return context.getClassLoader();
}
public ClassLoader getClassLoader(String packageName) {
Context context = createPackageContext(packageName);
return context.getClassLoader();
}
private void sendMessage(int what, Object obj) {
Message msg = Message.obtain();
msg.what = what;
msg.obj = obj;
mH.sendMessage(msg);
}
@Override
public IBinder getAppThread() {
return ActivityThread.getApplicationThread.call(VirtualCore.mainThread());
}
@Override
public IBinder getToken() {
return token;
}
public void initProcess(IBinder token, int vuid) {
this.token = token;
this.vuid = vuid;
}
private void handleNewIntent(NewIntentData data) {
Intent intent;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
intent = ReferrerIntent.ctor.newInstance(data.intent, data.creator);
} else {
intent = data.intent;
}
if (ActivityThread.performNewIntents != null) {
ActivityThread.performNewIntents.call(
VirtualCore.mainThread(),
data.token,
Collections.singletonList(intent)
);
} else {
if (BuildCompat.isQ()) {
ActivityThread.handleNewIntent.call(VirtualCore.mainThread(), data.token, Collections.singletonList(intent));
} else {
ActivityThreadNMR1.performNewIntents.call(
VirtualCore.mainThread(),
data.token,
Collections.singletonList(intent),
true);
}
}
}
public void bindApplicationForActivity(final String packageName, final String processName, final Intent intent) {
mUiCallback = VirtualCore.getUiCallback(intent);
bindApplication(packageName, processName);
}
public void bindApplication(final String packageName, final String processName) {
if (Looper.getMainLooper() == Looper.myLooper()) {
bindApplicationNoCheck(packageName, processName, new ConditionVariable());
} else {
final ConditionVariable lock = new ConditionVariable();
VirtualRuntime.getUIHandler().post(new Runnable() {
@Override
public void run() {
bindApplicationNoCheck(packageName, processName, lock);
lock.open();
}
});
lock.block();
}
}
private void bindApplicationNoCheck(String packageName, String processName, ConditionVariable lock) {
VDeviceInfo deviceInfo = getDeviceInfo();
if (processName == null) {
processName = packageName;
}
mTempLock = lock;
try {
setupUncaughtHandler();
} catch (Throwable e) {
e.printStackTrace();
}
try {
fixInstalledProviders();
} catch (Throwable e) {
e.printStackTrace();
}
mirror.android.os.Build.SERIAL.set(deviceInfo.serial);
mirror.android.os.Build.DEVICE.set(Build.DEVICE.replace(" ", "_"));
ActivityThread.mInitialApplication.set(
VirtualCore.mainThread(),
null
);
AppBindData data = new AppBindData();
InstalledAppInfo info = VirtualCore.get().getInstalledAppInfo(packageName, 0);
if (info == null) {
new Exception("App not exist!").printStackTrace();
Process.killProcess(0);
System.exit(0);
}
data.appInfo = VPackageManager.get().getApplicationInfo(packageName, 0, getUserId(vuid));
data.processName = processName;
data.appInfo.processName = processName;
data.providers = VPackageManager.get().queryContentProviders(processName, getVUid(), PackageManager.GET_META_DATA);
VLog.i(TAG, String.format("Binding application %s, (%s)", data.appInfo.packageName, data.processName));
mBoundApplication = data;
VirtualRuntime.setupRuntime(data.processName, data.appInfo);
int targetSdkVersion = data.appInfo.targetSdkVersion;
if (targetSdkVersion < Build.VERSION_CODES.GINGERBREAD) {
StrictMode.ThreadPolicy newPolicy = new StrictMode.ThreadPolicy.Builder(StrictMode.getThreadPolicy()).permitNetwork().build();
StrictMode.setThreadPolicy(newPolicy);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
mirror.android.os.Message.updateCheckRecycle.call(targetSdkVersion);
}
if (VASettings.ENABLE_IO_REDIRECT) {
startIOUniformer();
}
NativeEngine.launchEngine();
Object mainThread = VirtualCore.mainThread();
NativeEngine.startDexOverride();
Context context = createPackageContext(data.appInfo.packageName);
try {
// anti-virus, fuck ESET-NOD32: a variant of Android/AdDisplay.AdLock.AL potentially unwanted
// we can make direct call... use reflect to bypass.
// System.setProperty("java.io.tmpdir", context.getCacheDir().getAbsolutePath());
System.class.getDeclaredMethod("setProperty", String.class, String.class)
.invoke(null, "java.io.tmpdir", context.getCacheDir().getAbsolutePath());
} catch (Throwable ignored) {
VLog.e(TAG, "set tmp dir error:", ignored);
}
File codeCacheDir;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
codeCacheDir = context.getCodeCacheDir();
} else {
codeCacheDir = context.getCacheDir();
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
if (HardwareRenderer.setupDiskCache != null) {
HardwareRenderer.setupDiskCache.call(codeCacheDir);
}
} else {
if (ThreadedRenderer.setupDiskCache != null) {
ThreadedRenderer.setupDiskCache.call(codeCacheDir);
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (RenderScriptCacheDir.setupDiskCache != null) {
RenderScriptCacheDir.setupDiskCache.call(codeCacheDir);
}
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
if (RenderScript.setupDiskCache != null) {
RenderScript.setupDiskCache.call(codeCacheDir);
}
}
Object boundApp = fixBoundApp(mBoundApplication);
mBoundApplication.info = ContextImpl.mPackageInfo.get(context);
mirror.android.app.ActivityThread.AppBindData.info.set(boundApp, data.info);
VMRuntime.setTargetSdkVersion.call(VMRuntime.getRuntime.call(), data.appInfo.targetSdkVersion);
boolean conflict = SpecialComponentList.ConflictInstrumentation.isConflictingInstrumentation(packageName);
if (!conflict) {
InvocationStubManager.getInstance().checkEnv(AppInstrumentation.class);
}
ApplicationInfo applicationInfo = LoadedApk.mApplicationInfo.get(data.info);
if (Build.VERSION.SDK_INT >= 26 && applicationInfo.splitNames == null) {
applicationInfo.splitNames = new String[1];
}
boolean enableXposed = VirtualCore.get().isXposedEnabled();
if (enableXposed) {
VLog.i(TAG, "Xposed is enabled.");
ClassLoader originClassLoader = context.getClassLoader();
ExposedBridge.initOnce(context, data.appInfo, originClassLoader);
List modules = VirtualCore.get().getInstalledApps(0);
for (InstalledAppInfo module : modules) {
ExposedBridge.loadModule(module.apkPath, module.getOdexFile().getParent(), module.libPath,
data.appInfo, originClassLoader);
}
} else {
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);
}
if (data.providers != null) {
installContentProviders(mInitialApplication, data.providers);
}
if (lock != null) {
lock.open();
mTempLock = null;
}
VirtualCore.get().getComponentDelegate().beforeApplicationCreate(mInitialApplication);
try {
mInstrumentation.callApplicationOnCreate(mInitialApplication);
InvocationStubManager.getInstance().checkEnv(HCallbackStub.class);
if (conflict) {
InvocationStubManager.getInstance().checkEnv(AppInstrumentation.class);
}
Application createdApp = ActivityThread.mInitialApplication.get(mainThread);
if (createdApp != null) {
mInitialApplication = createdApp;
}
} catch (Exception e) {
if (!mInstrumentation.onException(mInitialApplication, e)) {
// 1. tell ui that do not need wait use now.
if (mUiCallback != null) {
try {
mUiCallback.onOpenFailed(packageName, VUserHandle.myUserId());
} catch (RemoteException ignored) {
}
}
// 2. tell vams that launch finish.
VActivityManager.get().appDoneExecuting();
// 3. rethrow
throw new RuntimeException(
"Unable to create application " + (mInitialApplication == null ? " [null application] " : mInitialApplication.getClass().getName())
+ ": " + e.toString(), e);
}
}
VActivityManager.get().appDoneExecuting();
VirtualCore.get().getComponentDelegate().afterApplicationCreate(mInitialApplication);
}
private void fixWeChatRecovery(Application app) {
try {
Field field = app.getClassLoader().loadClass("com.tencent.recovery.Recovery").getField("context");
field.setAccessible(true);
if (field.get(null) != null) {
return;
}
field.set(null, app.getBaseContext());
} catch (Throwable e) {
e.printStackTrace();
}
}
private void setupUncaughtHandler() {
ThreadGroup root = Thread.currentThread().getThreadGroup();
while (root.getParent() != null) {
root = root.getParent();
}
ThreadGroup newRoot = new RootThreadGroup(root);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
final List groups = mirror.java.lang.ThreadGroup.groups.get(root);
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (groups) {
List newGroups = new ArrayList<>(groups);
newGroups.remove(newRoot);
mirror.java.lang.ThreadGroup.groups.set(newRoot, newGroups);
groups.clear();
groups.add(newRoot);
mirror.java.lang.ThreadGroup.groups.set(root, groups);
for (ThreadGroup group : newGroups) {
if (group == newRoot) {
continue;
}
mirror.java.lang.ThreadGroup.parent.set(group, newRoot);
}
}
} else {
final ThreadGroup[] groups = ThreadGroupN.groups.get(root);
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (groups) {
ThreadGroup[] newGroups = groups.clone();
ThreadGroupN.groups.set(newRoot, newGroups);
ThreadGroupN.groups.set(root, new ThreadGroup[]{newRoot});
for (Object group : newGroups) {
if (group == newRoot) {
continue;
}
ThreadGroupN.parent.set(group, newRoot);
}
ThreadGroupN.ngroups.set(root, 1);
}
}
}
@SuppressLint("SdCardPath")
private void startIOUniformer() {
ApplicationInfo info = mBoundApplication.appInfo;
int userId = VUserHandle.myUserId();
String wifiMacAddressFile = deviceInfo.getWifiFile(userId).getPath();
NativeEngine.redirectDirectory("/sys/class/net/wlan0/address", wifiMacAddressFile);
NativeEngine.redirectDirectory("/sys/class/net/eth0/address", wifiMacAddressFile);
NativeEngine.redirectDirectory("/sys/class/net/wifi/address", wifiMacAddressFile);
NativeEngine.redirectDirectory("/data/data/" + info.packageName, info.dataDir);
NativeEngine.redirectDirectory("/data/user/0/" + info.packageName, info.dataDir);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
NativeEngine.redirectDirectory("/data/user_de/0/" + info.packageName, info.dataDir);
}
String libPath = VEnvironment.getAppLibDirectory(info.packageName).getAbsolutePath();
String userLibPath = new File(VEnvironment.getUserSystemDirectory(userId), info.packageName + "/lib").getAbsolutePath();
NativeEngine.redirectDirectory(userLibPath, libPath);
NativeEngine.redirectDirectory("/data/data/" + info.packageName + "/lib/", libPath);
NativeEngine.redirectDirectory("/data/user/0/" + info.packageName + "/lib/", libPath);
File dataUserLib = new File(VEnvironment.getDataUserPackageDirectory(userId, info.packageName), "lib");
if (!dataUserLib.exists()) {
try {
Os.symlink(libPath, dataUserLib.getPath());
} catch (ErrnoException e) {
VLog.w(TAG, "symlink error", e);
}
}
setupVirtualStorage(info, userId);
NativeEngine.enableIORedirect();
}
private void setupVirtualStorage(ApplicationInfo info, int userId) {
VirtualStorageManager vsManager = VirtualStorageManager.get();
boolean enable = vsManager.isVirtualStorageEnable(info.packageName, userId);
// 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;
}
File vsDir = VEnvironment.getVirtualStorageDir(info.packageName, userId);
if (vsDir == null || !vsDir.exists() || !vsDir.isDirectory()) {
return;
}
HashSet storageRoots = getMountPoints();
storageRoots.add(Environment.getExternalStorageDirectory().getAbsolutePath());
Set whiteList = new HashSet<>();
whiteList.add(Environment.DIRECTORY_PODCASTS);
whiteList.add(Environment.DIRECTORY_RINGTONES);
whiteList.add(Environment.DIRECTORY_ALARMS);
whiteList.add(Environment.DIRECTORY_NOTIFICATIONS);
whiteList.add(Environment.DIRECTORY_PICTURES);
whiteList.add(Environment.DIRECTORY_MOVIES);
whiteList.add(Environment.DIRECTORY_DOWNLOADS);
whiteList.add(Environment.DIRECTORY_DCIM);
// 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);
}
// ensure virtual storage white directory exists.
for (String whiteDir : whiteList) {
File originalDir = new File(Environment.getExternalStorageDirectory(), whiteDir);
File virtualDir = new File(vsDir, whiteDir);
if (!originalDir.exists()) {
continue;
}
//noinspection ResultOfMethodCallIgnored
virtualDir.mkdirs();
}
String vsPath = vsDir.getAbsolutePath();
NativeEngine.whitelist(vsPath, true);
String privatePath = VEnvironment.getVirtualPrivateStorageDir(userId).getAbsolutePath();
NativeEngine.whitelist(privatePath, true);
for (String storageRoot : storageRoots) {
for (String whiteDir : whiteList) {
// white list, do not redirect
String whitePath = new File(storageRoot, whiteDir).getAbsolutePath();
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);
}
}
@SuppressLint("SdCardPath")
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);
}
return mountPoints;
}
private Context createPackageContext(String packageName) {
try {
Context hostContext = VirtualCore.get().getContext();
return hostContext.createPackageContext(packageName, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
VirtualRuntime.crash(new RemoteException());
}
throw new RuntimeException();
}
private Object fixBoundApp(AppBindData data) {
Object thread = VirtualCore.mainThread();
Object boundApp = mirror.android.app.ActivityThread.mBoundApplication.get(thread);
mirror.android.app.ActivityThread.AppBindData.appInfo.set(boundApp, data.appInfo);
mirror.android.app.ActivityThread.AppBindData.processName.set(boundApp, data.processName);
mirror.android.app.ActivityThread.AppBindData.instrumentationName.set(
boundApp,
new ComponentName(data.appInfo.packageName, Instrumentation.class.getName())
);
ActivityThread.AppBindData.providers.set(boundApp, data.providers);
return boundApp;
}
private void installContentProviders(Context app, List providers) {
long origId = Binder.clearCallingIdentity();
Object mainThread = VirtualCore.mainThread();
try {
for (ProviderInfo cpi : providers) {
try {
ActivityThread.installProvider(mainThread, app, cpi, null);
} catch (Throwable e) {
e.printStackTrace();
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
@Override
public IBinder acquireProviderClient(ProviderInfo info) {
if (mTempLock != null) {
mTempLock.block();
}
if (!isBound()) {
VClientImpl.get().bindApplication(info.packageName, info.processName);
}
IInterface provider = null;
String[] authorities = info.authority.split(";");
String authority = authorities.length == 0 ? info.authority : authorities[0];
ContentResolver resolver = VirtualCore.get().getContext().getContentResolver();
ContentProviderClient client = null;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
client = resolver.acquireUnstableContentProviderClient(authority);
} else {
client = resolver.acquireContentProviderClient(authority);
}
} catch (Throwable e) {
VLog.e(TAG, "", e);
}
if (client != null) {
provider = mirror.android.content.ContentProviderClient.mContentProvider.get(client);
client.release();
}
return provider != null ? provider.asBinder() : null;
}
private void fixInstalledProviders() {
clearSettingProvider();
Map clientMap = ActivityThread.mProviderMap.get(VirtualCore.mainThread());
for (Object clientRecord : clientMap.values()) {
if (BuildCompat.isOreo()) {
IInterface provider = ActivityThread.ProviderClientRecordJB.mProvider.get(clientRecord);
Object holder = ActivityThread.ProviderClientRecordJB.mHolder.get(clientRecord);
if (holder == null) {
continue;
}
ProviderInfo info = ContentProviderHolderOreo.info.get(holder);
if (!info.authority.startsWith(VASettings.STUB_CP_AUTHORITY)) {
provider = ProviderHook.createProxy(true, info.authority, provider);
ActivityThread.ProviderClientRecordJB.mProvider.set(clientRecord, provider);
ContentProviderHolderOreo.provider.set(holder, provider);
}
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
IInterface provider = ActivityThread.ProviderClientRecordJB.mProvider.get(clientRecord);
Object holder = ActivityThread.ProviderClientRecordJB.mHolder.get(clientRecord);
if (holder == null) {
continue;
}
ProviderInfo info = IActivityManager.ContentProviderHolder.info.get(holder);
if (!info.authority.startsWith(VASettings.STUB_CP_AUTHORITY)) {
provider = ProviderHook.createProxy(true, info.authority, provider);
ActivityThread.ProviderClientRecordJB.mProvider.set(clientRecord, provider);
IActivityManager.ContentProviderHolder.provider.set(holder, provider);
}
} else {
String authority = ActivityThread.ProviderClientRecord.mName.get(clientRecord);
IInterface provider = ActivityThread.ProviderClientRecord.mProvider.get(clientRecord);
if (provider != null && !authority.startsWith(VASettings.STUB_CP_AUTHORITY)) {
provider = ProviderHook.createProxy(true, authority, provider);
ActivityThread.ProviderClientRecord.mProvider.set(clientRecord, provider);
}
}
}
}
private void clearSettingProvider() {
Object cache;
cache = Settings.System.sNameValueCache.get();
if (cache != null) {
clearContentProvider(cache);
}
cache = Settings.Secure.sNameValueCache.get();
if (cache != null) {
clearContentProvider(cache);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && Settings.Global.TYPE != null) {
cache = Settings.Global.sNameValueCache.get();
if (cache != null) {
clearContentProvider(cache);
}
}
}
private static void clearContentProvider(Object cache) {
if (BuildCompat.isOreo()) {
Object holder = Settings.NameValueCacheOreo.mProviderHolder.get(cache);
if (holder != null) {
Settings.ContentProviderHolder.mContentProvider.set(holder, null);
}
} else {
Settings.NameValueCache.mContentProvider.set(cache, null);
}
}
@Override
public void finishActivity(IBinder token) {
VActivityManager.get().finishActivity(token);
}
@Override
public void scheduleNewIntent(String creator, IBinder token, Intent intent) {
NewIntentData data = new NewIntentData();
data.creator = creator;
data.token = token;
data.intent = intent;
sendMessage(NEW_INTENT, data);
}
@Override
public void scheduleReceiver(String processName, ComponentName component, Intent intent, PendingResultData resultData) {
ReceiverData receiverData = new ReceiverData();
receiverData.resultData = resultData;
receiverData.intent = intent;
receiverData.component = component;
receiverData.processName = processName;
sendMessage(RECEIVER, receiverData);
}
private void handleReceiver(ReceiverData data) {
BroadcastReceiver.PendingResult result = data.resultData.build();
try {
if (!isBound()) {
bindApplication(data.component.getPackageName(), data.processName);
}
Context context = mInitialApplication.getBaseContext();
Context receiverContext = ContextImpl.getReceiverRestrictedContext.call(context);
String className = data.component.getClassName();
BroadcastReceiver receiver = (BroadcastReceiver) context.getClassLoader().loadClass(className).newInstance();
mirror.android.content.BroadcastReceiver.setPendingResult.call(receiver, result);
data.intent.setExtrasClassLoader(context.getClassLoader());
if (data.intent.getComponent() == null) {
data.intent.setComponent(data.component);
}
receiver.onReceive(receiverContext, data.intent);
if (mirror.android.content.BroadcastReceiver.getPendingResult.call(receiver) != null) {
result.finish();
}
} catch (Exception e) {
// must be this for misjudge of anti-virus!!
throw new RuntimeException(String.format("Unable to start receiver: %s ", data.component), e);
}
VActivityManager.get().broadcastFinish(data.resultData);
}
@Override
public IBinder createProxyService(ComponentName component, IBinder binder) {
return ProxyServiceFactory.getProxyService(getCurrentApplication(), component, binder);
}
@Override
public String getDebugInfo() {
return "process : " + VirtualRuntime.getProcessName() + "\n" +
"initialPkg : " + VirtualRuntime.getInitialPackageName() + "\n" +
"vuid : " + vuid;
}
private static class RootThreadGroup extends ThreadGroup {
RootThreadGroup(ThreadGroup parent) {
super(parent, "VA-Root");
}
@Override
public void uncaughtException(Thread t, Throwable e) {
CrashHandler handler = VClientImpl.gClient.crashHandler;
if (handler != null) {
handler.handleUncaughtException(t, e);
} else {
VLog.e("uncaught", e);
System.exit(0);
}
}
}
private final class NewIntentData {
String creator;
IBinder token;
Intent intent;
}
private final class AppBindData {
String processName;
ApplicationInfo appInfo;
List providers;
Object info;
}
private final class ReceiverData {
PendingResultData resultData;
Intent intent;
ComponentName component;
String processName;
}
private class H extends Handler {
private H() {
super(Looper.getMainLooper());
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case NEW_INTENT: {
handleNewIntent((NewIntentData) msg.obj);
}
break;
case RECEIVER: {
handleReceiver((ReceiverData) msg.obj);
}
}
}
}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/badger/BadgerManager.java
================================================
package com.lody.virtual.client.badger;
import android.content.Intent;
import com.lody.virtual.client.ipc.VActivityManager;
import com.lody.virtual.remote.BadgerInfo;
import java.util.HashMap;
import java.util.Map;
/**
* @author Lody
*/
public class BadgerManager {
private static final Map BADGERS = new HashMap<>(10);
static {
addBadger(new BroadcastBadger1.AdwHomeBadger());
addBadger(new BroadcastBadger1.AospHomeBadger());
addBadger(new BroadcastBadger1.LGHomeBadger());
addBadger(new BroadcastBadger1.NewHtcHomeBadger2());
addBadger(new BroadcastBadger1.OPPOHomeBader());
addBadger(new BroadcastBadger2.NewHtcHomeBadger1());
}
private static void addBadger(IBadger badger) {
BADGERS.put(badger.getAction(), badger);
}
public static boolean handleBadger(Intent intent) {
IBadger badger = BADGERS.get(intent.getAction());
if (badger != null) {
BadgerInfo info = badger.handleBadger(intent);
VActivityManager.get().notifyBadgerChange(info);
return true;
}
return false;
}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/badger/BroadcastBadger1.java
================================================
package com.lody.virtual.client.badger;
import android.content.Intent;
import com.lody.virtual.remote.BadgerInfo;
/**
* @author Lody
*/
public abstract class BroadcastBadger1 implements IBadger {
public abstract String getAction();
public abstract String getPackageKey();
public abstract String getClassNameKey();
public abstract String getCountKey();
@Override
public BadgerInfo handleBadger(Intent intent) {
BadgerInfo info = new BadgerInfo();
info.packageName = intent.getStringExtra(getPackageKey());
if (getClassNameKey() != null) {
info.className = intent.getStringExtra(getClassNameKey());
}
info.badgerCount = intent.getIntExtra(getCountKey(), 0);
return info;
}
static class LGHomeBadger extends BroadcastBadger1 {
@Override
public String getAction() {
return "android.intent.action.BADGE_COUNT_UPDATE";
}
@Override
public String getPackageKey() {
return "badge_count_package_name";
}
@Override
public String getClassNameKey() {
return "badge_count_class_name";
}
@Override
public String getCountKey() {
return "badge_count";
}
}
static class AdwHomeBadger extends BroadcastBadger1 {
@Override
public String getAction() {
return "org.adw.launcher.counter.SEND";
}
@Override
public String getPackageKey() {
return "PNAME";
}
@Override
public String getClassNameKey() {
return "CNAME";
}
@Override
public String getCountKey() {
return "COUNT";
}
}
static class AospHomeBadger extends BroadcastBadger1 {
@Override
public String getAction() {
return "android.intent.action.BADGE_COUNT_UPDATE";
}
@Override
public String getPackageKey() {
return "badge_count_package_name";
}
@Override
public String getClassNameKey() {
return "badge_count_class_name";
}
@Override
public String getCountKey() {
return "badge_count";
}
}
static class NewHtcHomeBadger2 extends BroadcastBadger1 {
@Override
public String getAction() {
return "com.htc.launcher.action.UPDATE_SHORTCUT";
}
@Override
public String getPackageKey() {
return "packagename";
}
@Override
public String getClassNameKey() {
return null;
}
@Override
public String getCountKey() {
return "count";
}
}
static class OPPOHomeBader extends BroadcastBadger1 {
@Override
public String getAction() {
return "com.oppo.unsettledevent";
}
@Override
public String getPackageKey() {
return "pakeageName";
}
@Override
public String getClassNameKey() {
return null;
}
@Override
public String getCountKey() {
return "number";
}
}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/badger/BroadcastBadger2.java
================================================
package com.lody.virtual.client.badger;
import android.content.ComponentName;
import android.content.Intent;
import com.lody.virtual.remote.BadgerInfo;
/**
* @author Lody
*/
public abstract class BroadcastBadger2 implements IBadger {
public abstract String getAction();
public abstract String getComponentKey();
public abstract String getCountKey();
@Override
public BadgerInfo handleBadger(Intent intent) {
BadgerInfo info = new BadgerInfo();
String componentName = intent.getStringExtra(getComponentKey());
ComponentName component = ComponentName.unflattenFromString(componentName);
if (component != null) {
info.packageName = component.getPackageName();
info.className = component.getClassName();
info.badgerCount = intent.getIntExtra(getCountKey(), 0);
return info;
}
return null;
}
static class NewHtcHomeBadger1 extends BroadcastBadger2 {
@Override
public String getAction() {
return "com.htc.launcher.action.SET_NOTIFICATION";
}
@Override
public String getComponentKey() {
return "com.htc.launcher.extra.COMPONENT";
}
@Override
public String getCountKey() {
return "com.htc.launcher.extra.COUNT";
}
}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/badger/IBadger.java
================================================
package com.lody.virtual.client.badger;
import android.content.Intent;
import com.lody.virtual.remote.BadgerInfo;
/**
* @author Lody
*/
public interface IBadger {
String getAction();
BadgerInfo handleBadger(Intent intent);
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/core/CrashHandler.java
================================================
package com.lody.virtual.client.core;
/**
* @author Lody
*/
public interface CrashHandler {
void handleUncaughtException(Thread t, Throwable e);
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/core/InstallStrategy.java
================================================
package com.lody.virtual.client.core;
/**
* @author Lody
*
*
*/
public interface InstallStrategy {
int TERMINATE_IF_EXIST = 0x01 << 1;
int UPDATE_IF_EXIST = 0x01 << 2;
int COMPARE_VERSION = 0X01 << 3;
int IGNORE_NEW_VERSION = 0x01 << 4;
int DEPEND_SYSTEM_IF_EXIST = 0x01 << 5;
int SKIP_DEX_OPT = 0x01 << 6;
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/core/InvocationStubManager.java
================================================
package com.lody.virtual.client.core;
import android.os.Build;
import com.lody.virtual.client.hook.base.MethodInvocationProxy;
import com.lody.virtual.client.hook.base.MethodInvocationStub;
import com.lody.virtual.client.hook.delegate.AppInstrumentation;
import com.lody.virtual.client.hook.proxies.account.AccountManagerStub;
import com.lody.virtual.client.hook.proxies.alarm.AlarmManagerStub;
import com.lody.virtual.client.hook.proxies.am.ActivityManagerStub;
import com.lody.virtual.client.hook.proxies.am.ActivityTaskManagerStub;
import com.lody.virtual.client.hook.proxies.am.HCallbackStub;
import com.lody.virtual.client.hook.proxies.am.TransactionHandlerStub;
import com.lody.virtual.client.hook.proxies.appops.AppOpsManagerStub;
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;
import com.lody.virtual.client.hook.proxies.content.ContentServiceStub;
import com.lody.virtual.client.hook.proxies.context_hub.ContextHubServiceStub;
import com.lody.virtual.client.hook.proxies.devicepolicy.DevicePolicyManagerStub;
import com.lody.virtual.client.hook.proxies.display.DisplayStub;
import com.lody.virtual.client.hook.proxies.dropbox.DropBoxManagerStub;
import com.lody.virtual.client.hook.proxies.fingerprint.FingerprintManagerStub;
import com.lody.virtual.client.hook.proxies.graphics.GraphicsStatsStub;
import com.lody.virtual.client.hook.proxies.imms.MmsStub;
import com.lody.virtual.client.hook.proxies.input.InputMethodManagerStub;
import com.lody.virtual.client.hook.proxies.isms.ISmsStub;
import com.lody.virtual.client.hook.proxies.isub.ISubStub;
import com.lody.virtual.client.hook.proxies.job.JobServiceStub;
import com.lody.virtual.client.hook.proxies.libcore.LibCoreStub;
import com.lody.virtual.client.hook.proxies.location.LocationManagerStub;
import com.lody.virtual.client.hook.proxies.media.router.MediaRouterServiceStub;
import com.lody.virtual.client.hook.proxies.media.session.SessionManagerStub;
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;
import com.lody.virtual.client.hook.proxies.pm.PackageManagerStub;
import com.lody.virtual.client.hook.proxies.power.PowerManagerStub;
import com.lody.virtual.client.hook.proxies.restriction.RestrictionStub;
import com.lody.virtual.client.hook.proxies.search.SearchManagerStub;
import com.lody.virtual.client.hook.proxies.shortcut.ShortcutServiceStub;
import com.lody.virtual.client.hook.proxies.telephony.TelephonyRegistryStub;
import com.lody.virtual.client.hook.proxies.telephony.TelephonyStub;
import com.lody.virtual.client.hook.proxies.usage.UsageStatsManagerStub;
import com.lody.virtual.client.hook.proxies.user.UserManagerStub;
import com.lody.virtual.client.hook.proxies.vibrator.VibratorStub;
import com.lody.virtual.client.hook.proxies.view.AutoFillManagerStub;
import com.lody.virtual.client.hook.proxies.wifi.WifiManagerStub;
import com.lody.virtual.client.hook.proxies.wifi_scanner.WifiScannerStub;
import com.lody.virtual.client.hook.proxies.window.WindowManagerStub;
import com.lody.virtual.client.interfaces.IInjector;
import com.lody.virtual.helper.compat.BuildCompat;
import java.util.HashMap;
import java.util.Map;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
import static android.os.Build.VERSION_CODES.KITKAT;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1;
import static android.os.Build.VERSION_CODES.M;
import static android.os.Build.VERSION_CODES.N;
/**
* @author Lody
*
*/
public final class InvocationStubManager {
private static InvocationStubManager sInstance = new InvocationStubManager();
private static boolean sInit;
private Map, IInjector> mInjectors = new HashMap<>(13);
private InvocationStubManager() {
}
public static InvocationStubManager getInstance() {
return sInstance;
}
void injectAll() throws Throwable {
for (IInjector injector : mInjectors.values()) {
injector.inject();
}
// XXX: Lazy inject the Instrumentation,
addInjector(AppInstrumentation.getDefault());
}
/**
* @return if the InvocationStubManager has been initialized.
*/
public boolean isInit() {
return sInit;
}
public void init() throws Throwable {
if (isInit()) {
throw new IllegalStateException("InvocationStubManager Has been initialized.");
}
injectInternal();
sInit = true;
}
private void injectInternal() throws Throwable {
if (VirtualCore.get().isMainProcess()) {
return;
}
if (VirtualCore.get().isServerProcess()) {
addInjector(new ActivityManagerStub());
addInjector(new PackageManagerStub());
return;
}
if (VirtualCore.get().isVAppProcess()) {
addInjector(new LibCoreStub());
addInjector(new ActivityManagerStub());
addInjector(new PackageManagerStub());
if (Build.VERSION.SDK_INT >= 28) {
addInjector(new TransactionHandlerStub());
}
addInjector(HCallbackStub.getDefault());
addInjector(new ISmsStub());
addInjector(new ISubStub());
addInjector(new DropBoxManagerStub());
addInjector(new NotificationManagerStub());
addInjector(new LocationManagerStub());
addInjector(new WindowManagerStub());
addInjector(new ClipBoardStub());
addInjector(new MountServiceStub());
addInjector(new BackupManagerStub());
addInjector(new TelephonyStub());
addInjector(new TelephonyRegistryStub());
addInjector(new PhoneSubInfoStub());
addInjector(new PowerManagerStub());
addInjector(new AppWidgetManagerStub());
addInjector(new AccountManagerStub());
addInjector(new AudioManagerStub());
addInjector(new SearchManagerStub());
addInjector(new ContentServiceStub());
addInjector(new ConnectivityStub());
if (Build.VERSION.SDK_INT >= JELLY_BEAN_MR2) {
addInjector(new VibratorStub());
addInjector(new WifiManagerStub());
addInjector(new BluetoothStub());
addInjector(new ContextHubServiceStub());
}
if (Build.VERSION.SDK_INT >= JELLY_BEAN_MR1) {
addInjector(new UserManagerStub());
}
if (Build.VERSION.SDK_INT >= JELLY_BEAN_MR1) {
addInjector(new DisplayStub());
}
if (Build.VERSION.SDK_INT >= LOLLIPOP) {
addInjector(new PersistentDataBlockServiceStub());
addInjector(new InputMethodManagerStub());
addInjector(new MmsStub());
addInjector(new SessionManagerStub());
addInjector(new JobServiceStub());
addInjector(new RestrictionStub());
}
if (Build.VERSION.SDK_INT >= KITKAT) {
addInjector(new AlarmManagerStub());
addInjector(new AppOpsManagerStub());
addInjector(new MediaRouterServiceStub());
}
if (Build.VERSION.SDK_INT >= LOLLIPOP_MR1) {
addInjector(new GraphicsStatsStub());
addInjector(new UsageStatsManagerStub());
addInjector(new LauncherAppsStub());
}
if (Build.VERSION.SDK_INT >= M) {
addInjector(new FingerprintManagerStub());
addInjector(new NetworkManagementStub());
}
if (Build.VERSION.SDK_INT >= N) {
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());
}
}
}
private void addInjector(IInjector IInjector) {
mInjectors.put(IInjector.getClass(), IInjector);
}
public T findInjector(Class clazz) {
// noinspection unchecked
return (T) mInjectors.get(clazz);
}
public void checkEnv(Class clazz) {
IInjector IInjector = findInjector(clazz);
if (IInjector != null && IInjector.isEnvBad()) {
try {
IInjector.inject();
} catch (Throwable e) {
e.printStackTrace();
}
}
}
public H getInvocationStub(Class injectorClass) {
T injector = findInjector(injectorClass);
if (injector != null && injector instanceof MethodInvocationProxy) {
// noinspection unchecked
return (H) ((MethodInvocationProxy) injector).getInvocationStub();
}
return null;
}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/core/VirtualCore.java
================================================
package com.lody.virtual.client.core;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
import android.text.TextUtils;
import android.widget.Toast;
import com.lody.virtual.R;
import com.lody.virtual.client.VClientImpl;
import com.lody.virtual.client.env.Constants;
import com.lody.virtual.client.env.VirtualRuntime;
import com.lody.virtual.client.fixer.ContextFixer;
import com.lody.virtual.client.hook.delegate.ComponentDelegate;
import com.lody.virtual.client.hook.delegate.PhoneInfoDelegate;
import com.lody.virtual.client.hook.delegate.TaskDescriptionDelegate;
import com.lody.virtual.client.ipc.LocalProxyUtils;
import com.lody.virtual.client.ipc.ServiceManagerNative;
import com.lody.virtual.client.ipc.VActivityManager;
import com.lody.virtual.client.ipc.VPackageManager;
import com.lody.virtual.client.stub.VASettings;
import com.lody.virtual.helper.compat.BundleCompat;
import com.lody.virtual.helper.utils.BitmapUtils;
import com.lody.virtual.os.VUserHandle;
import com.lody.virtual.remote.InstallResult;
import com.lody.virtual.remote.InstalledAppInfo;
import com.lody.virtual.server.IAppManager;
import com.lody.virtual.server.interfaces.IAppRequestListener;
import com.lody.virtual.server.interfaces.IPackageObserver;
import com.lody.virtual.server.interfaces.IUiCallback;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import me.weishu.reflection.Reflection;
import mirror.android.app.ActivityThread;
/**
* @author Lody
* @version 3.5
*/
public final class VirtualCore {
public static final int GET_HIDDEN_APP = 0x00000001;
public static final String TAICHI_PACKAGE = "me.weishu.exp";
@SuppressLint("StaticFieldLeak")
private static VirtualCore gCore = new VirtualCore();
private final int myUid = Process.myUid();
/**
* Client Package Manager
*/
private PackageManager unHookPackageManager;
/**
* Host package name
*/
private String hostPkgName;
/**
* ActivityThread instance
*/
private Object mainThread;
private Context context;
/**
* Main ProcessName
*/
private String mainProcessName;
/**
* Real Process Name
*/
private String processName;
private ProcessType processType;
private IAppManager mService;
private boolean isStartUp;
private PackageInfo hostPkgInfo;
private int systemPid;
private ConditionVariable initLock = new ConditionVariable();
private PhoneInfoDelegate phoneInfoDelegate;
private ComponentDelegate componentDelegate;
private TaskDescriptionDelegate taskDescriptionDelegate;
private VirtualCore() {
}
public static VirtualCore get() {
return gCore;
}
public static PackageManager getPM() {
return get().getPackageManager();
}
public static Object mainThread() {
return get().mainThread;
}
public ConditionVariable getInitLock() {
return initLock;
}
public int myUid() {
return myUid;
}
public int myUserId() {
return VUserHandle.getUserId(myUid);
}
public ComponentDelegate getComponentDelegate() {
return componentDelegate == null ? ComponentDelegate.EMPTY : componentDelegate;
}
public void setComponentDelegate(ComponentDelegate delegate) {
this.componentDelegate = delegate;
}
public PhoneInfoDelegate getPhoneInfoDelegate() {
return phoneInfoDelegate;
}
public void setPhoneInfoDelegate(PhoneInfoDelegate phoneInfoDelegate) {
this.phoneInfoDelegate = phoneInfoDelegate;
}
public void setCrashHandler(CrashHandler handler) {
VClientImpl.get().setCrashHandler(handler);
}
public TaskDescriptionDelegate getTaskDescriptionDelegate() {
return taskDescriptionDelegate;
}
public void setTaskDescriptionDelegate(TaskDescriptionDelegate taskDescriptionDelegate) {
this.taskDescriptionDelegate = taskDescriptionDelegate;
}
public int[] getGids() {
return hostPkgInfo.gids;
}
public Context getContext() {
return context;
}
public PackageManager getPackageManager() {
return context.getPackageManager();
}
public String getHostPkg() {
return hostPkgName;
}
public PackageManager getUnHookPackageManager() {
return unHookPackageManager;
}
public void startup(Context context) throws Throwable {
if (!isStartUp) {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new IllegalStateException("VirtualCore.startup() must called in main thread.");
}
Reflection.unseal(context);
VASettings.STUB_CP_AUTHORITY = context.getPackageName() + "." + VASettings.STUB_DEF_AUTHORITY;
ServiceManagerNative.SERVICE_CP_AUTH = context.getPackageName() + "." + ServiceManagerNative.SERVICE_DEF_AUTH;
this.context = context;
mainThread = ActivityThread.currentActivityThread.call();
unHookPackageManager = context.getPackageManager();
hostPkgInfo = unHookPackageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_PROVIDERS);
detectProcessType();
InvocationStubManager invocationStubManager = InvocationStubManager.getInstance();
invocationStubManager.init();
invocationStubManager.injectAll();
ContextFixer.fixContext(context);
isStartUp = true;
if (initLock != null) {
initLock.open();
initLock = null;
}
}
}
public void waitForEngine() {
ServiceManagerNative.ensureServerStarted();
}
public boolean isEngineLaunched() {
String engineProcessName = getEngineProcessName();
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningAppProcessInfo info : am.getRunningAppProcesses()) {
if (info.processName.endsWith(engineProcessName)) {
return true;
}
}
return false;
}
public String getEngineProcessName() {
return context.getString(R.string.engine_process_name);
}
public void initialize(VirtualInitializer initializer) {
if (initializer == null) {
throw new IllegalStateException("Initializer = NULL");
}
switch (processType) {
case Main:
initializer.onMainProcess();
break;
case VAppClient:
initializer.onVirtualProcess();
break;
case Server:
initializer.onServerProcess();
break;
case CHILD:
initializer.onChildProcess();
break;
}
}
private void detectProcessType() {
// Host package name
hostPkgName = context.getApplicationInfo().packageName;
// Main process name
mainProcessName = context.getApplicationInfo().processName;
// Current process name
processName = ActivityThread.getProcessName.call(mainThread);
if (processName.equals(mainProcessName)) {
processType = ProcessType.Main;
} else if (processName.endsWith(Constants.SERVER_PROCESS_NAME)) {
processType = ProcessType.Server;
} else if (VActivityManager.get().isAppProcess(processName)) {
processType = ProcessType.VAppClient;
} else {
processType = ProcessType.CHILD;
}
if (isVAppProcess()) {
systemPid = VActivityManager.get().getSystemPid();
}
}
private IAppManager getService() {
if (mService == null
|| (!VirtualCore.get().isVAppProcess() && !mService.asBinder().pingBinder())) {
synchronized (this) {
Object remote = getStubInterface();
mService = LocalProxyUtils.genProxy(IAppManager.class, remote);
}
}
return mService;
}
private Object getStubInterface() {
return IAppManager.Stub
.asInterface(ServiceManagerNative.getService(ServiceManagerNative.APP));
}
/**
* @return If the current process is used to VA.
*/
public boolean isVAppProcess() {
return ProcessType.VAppClient == processType;
}
/**
* @return If the current process is the main.
*/
public boolean isMainProcess() {
return ProcessType.Main == processType;
}
/**
* @return If the current process is the child.
*/
public boolean isChildProcess() {
return ProcessType.CHILD == processType;
}
/**
* @return If the current process is the server.
*/
public boolean isServerProcess() {
return ProcessType.Server == processType;
}
/**
* @return the actual process name
*/
public String getProcessName() {
return processName;
}
/**
* @return the Main process name
*/
public String getMainProcessName() {
return mainProcessName;
}
/**
* Optimize the Dalvik-Cache for the specified package.
*
* @param pkg package name
* @throws IOException
*/
@Deprecated
public void preOpt(String pkg) throws IOException {
/*
InstalledAppInfo info = getInstalledAppInfo(pkg, 0);
if (info != null && !info.dependSystem) {
DexFile.loadDex(info.apkPath, info.getOdexFile().getPath(), 0).close();
}*/
}
/**
* Is the specified app running in foreground / background?
*
* @param packageName package name
* @param userId user id
* @return if the specified app running in foreground / background.
*/
public boolean isAppRunning(String packageName, int userId) {
return VActivityManager.get().isAppRunning(packageName, userId);
}
public InstallResult installPackage(String apkPath, int flags) {
try {
return getService().installPackage(apkPath, flags);
} catch (RemoteException e) {
return VirtualRuntime.crash(e);
}
}
public boolean clearPackage(String packageName) {
try {
return getService().clearPackage(packageName);
} catch (RemoteException e) {
return VirtualRuntime.crash(e);
}
}
public boolean clearPackageAsUser(int userId, String packageName) {
try {
return getService().clearPackageAsUser(userId, packageName);
} catch (RemoteException e) {
return VirtualRuntime.crash(e);
}
}
public void addVisibleOutsidePackage(String pkg) {
try {
getService().addVisibleOutsidePackage(pkg);
} catch (RemoteException e) {
VirtualRuntime.crash(e);
}
}
public void removeVisibleOutsidePackage(String pkg) {
try {
getService().removeVisibleOutsidePackage(pkg);
} catch (RemoteException e) {
VirtualRuntime.crash(e);
}
}
public boolean isOutsidePackageVisible(String pkg) {
try {
return getService().isOutsidePackageVisible(pkg);
} catch (RemoteException e) {
return VirtualRuntime.crash(e);
}
}
public boolean isXposedEnabled() {
return !VirtualCore.get().getContext().getFileStreamPath(".disable_xposed").exists();
}
public boolean isAppInstalled(String pkg) {
try {
return getService().isAppInstalled(pkg);
} catch (RemoteException e) {
return VirtualRuntime.crash(e);
}
}
public boolean isPackageLaunchable(String packageName) {
InstalledAppInfo info = getInstalledAppInfo(packageName, 0);
return info != null
&& getLaunchIntent(packageName, info.getInstalledUsers()[0]) != null;
}
public Intent getLaunchIntent(String packageName, int userId) {
VPackageManager pm = VPackageManager.get();
Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
intentToResolve.addCategory(Intent.CATEGORY_INFO);
intentToResolve.setPackage(packageName);
List ris = pm.queryIntentActivities(intentToResolve, intentToResolve.resolveType(context), 0, userId);
// Otherwise, try to find a main launcher activity.
if (ris == null || ris.size() <= 0) {
// reuse the intent instance
intentToResolve.removeCategory(Intent.CATEGORY_INFO);
intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
intentToResolve.setPackage(packageName);
ris = pm.queryIntentActivities(intentToResolve, intentToResolve.resolveType(context), 0, userId);
}
if (ris == null || ris.size() <= 0) {
return null;
}
ActivityInfo activityInfo = null;
for (ResolveInfo resolveInfo : ris) {
if (resolveInfo.activityInfo.enabled) {
// select the first enabled component
activityInfo = resolveInfo.activityInfo;
break;
}
}
if (activityInfo == null) {
activityInfo = ris.get(0).activityInfo;
}
Intent intent = new Intent(intentToResolve);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClassName(activityInfo.packageName,
activityInfo.name);
return intent;
}
public boolean createShortcut(int userId, String packageName, OnEmitShortcutListener listener) {
return createShortcut(userId, packageName, null, listener);
}
public boolean createShortcut(int userId, String packageName, Intent splash, OnEmitShortcutListener listener) {
InstalledAppInfo setting = getInstalledAppInfo(packageName, 0);
if (setting == null) {
return false;
}
ApplicationInfo appInfo = setting.getApplicationInfo(userId);
PackageManager pm = context.getPackageManager();
String name;
Bitmap icon;
String id = packageName + userId;
try {
CharSequence sequence = appInfo.loadLabel(pm);
name = sequence.toString();
icon = BitmapUtils.drawableToBitmap(appInfo.loadIcon(pm));
} catch (Throwable e) {
return false;
}
if (listener != null) {
String newName = listener.getName(name);
if (newName != null) {
name = newName;
}
Bitmap newIcon = listener.getIcon(icon);
if (newIcon != null) {
icon = newIcon;
}
}
Intent targetIntent = getLaunchIntent(packageName, userId);
if (targetIntent == null) {
return false;
}
Intent shortcutIntent = new Intent(Intent.ACTION_VIEW);
shortcutIntent.setClassName(getHostPkg(), Constants.SHORTCUT_PROXY_ACTIVITY_NAME);
shortcutIntent.addCategory(Intent.CATEGORY_DEFAULT);
if (splash != null) {
shortcutIntent.putExtra("_VA_|_splash_", splash.toUri(0));
}
shortcutIntent.putExtra("_VA_|_intent_", targetIntent);
shortcutIntent.putExtra("_VA_|_uri_", targetIntent.toUri(0));
shortcutIntent.putExtra("_VA_|_user_id_", userId);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
// bad parcel.
shortcutIntent.removeExtra("_VA_|_intent_");
Icon withBitmap = Icon.createWithBitmap(icon);
ShortcutInfo likeShortcut = new ShortcutInfo.Builder(context, id)
.setShortLabel(name)
.setLongLabel(name)
.setIcon(withBitmap)
.setIntent(shortcutIntent)
.build();
// crate app shortcuts.
createShortcutAboveN(context, likeShortcut);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return createDeskShortcutAboveO(context, likeShortcut);
}
}
Intent addIntent = new Intent();
addIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon);
addIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
try {
context.sendBroadcast(addIntent);
} catch (Throwable ignored) {
return false;
}
return true;
}
@TargetApi(Build.VERSION_CODES.N_MR1)
private static boolean createShortcutAboveN(Context context, ShortcutInfo likeShortcut) {
ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);
if (shortcutManager == null) {
return false;
}
try {
int max = shortcutManager.getMaxShortcutCountPerActivity();
List dynamicShortcuts = shortcutManager.getDynamicShortcuts();
if (dynamicShortcuts.size() >= max) {
Collections.sort(dynamicShortcuts, new Comparator() {
@Override
public int compare(ShortcutInfo o1, ShortcutInfo o2) {
long r = o1.getLastChangedTimestamp() - o2.getLastChangedTimestamp();
return r == 0 ? 0 : (r > 0 ? 1 : -1);
}
});
ShortcutInfo remove = dynamicShortcuts.remove(0);// remove old.
shortcutManager.removeDynamicShortcuts(Collections.singletonList(remove.getId()));
}
shortcutManager.addDynamicShortcuts(Collections.singletonList(likeShortcut));
return true;
} catch (Throwable e) {
return false;
}
}
@TargetApi(Build.VERSION_CODES.O)
private static boolean createDeskShortcutAboveO(Context context, ShortcutInfo info) {
ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class);
if (shortcutManager == null) {
return false;
}
if (shortcutManager.isRequestPinShortcutSupported()) {
// 当添加快捷方式的确认弹框弹出来时,将被回调
// PendingIntent shortcutCallbackIntent = PendingIntent.getBroadcast(context, 0,
// new Intent(context, MyReceiver.class), PendingIntent.FLAG_UPDATE_CURRENT);
List pinnedShortcuts = shortcutManager.getPinnedShortcuts();
boolean exists = false;
for (ShortcutInfo pinnedShortcut : pinnedShortcuts) {
if (TextUtils.equals(pinnedShortcut.getId(), info.getId())) {
// already exist.
exists = true;
Toast.makeText(context, R.string.create_shortcut_already_exist, Toast.LENGTH_SHORT).show();
break;
}
}
if (!exists) {
shortcutManager.requestPinShortcut(info, null);
}
return true;
}
return false;
}
public boolean removeShortcut(int userId, String packageName, Intent splash, OnEmitShortcutListener listener) {
InstalledAppInfo setting = getInstalledAppInfo(packageName, 0);
if (setting == null) {
return false;
}
ApplicationInfo appInfo = setting.getApplicationInfo(userId);
PackageManager pm = context.getPackageManager();
String name;
try {
CharSequence sequence = appInfo.loadLabel(pm);
name = sequence.toString();
} catch (Throwable e) {
return false;
}
if (listener != null) {
String newName = listener.getName(name);
if (newName != null) {
name = newName;
}
}
Intent targetIntent = getLaunchIntent(packageName, userId);
if (targetIntent == null) {
return false;
}
Intent shortcutIntent = new Intent();
shortcutIntent.setClassName(getHostPkg(), Constants.SHORTCUT_PROXY_ACTIVITY_NAME);
shortcutIntent.addCategory(Intent.CATEGORY_DEFAULT);
if (splash != null) {
shortcutIntent.putExtra("_VA_|_splash_", splash.toUri(0));
}
shortcutIntent.putExtra("_VA_|_intent_", targetIntent);
shortcutIntent.putExtra("_VA_|_uri_", targetIntent.toUri(0));
shortcutIntent.putExtra("_VA_|_user_id_", VUserHandle.myUserId());
Intent addIntent = new Intent();
addIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
addIntent.setAction("com.android.launcher.action.UNINSTALL_SHORTCUT");
context.sendBroadcast(addIntent);
return true;
}
public abstract static class UiCallback extends IUiCallback.Stub {
}
public void setUiCallback(Intent intent, IUiCallback callback) {
if (callback != null) {
Bundle bundle = new Bundle();
BundleCompat.putBinder(bundle, "_VA_|_ui_callback_", callback.asBinder());
intent.putExtra("_VA_|_sender_", bundle);
}
}
public static IUiCallback getUiCallback(Intent intent) {
if (intent == null) {
return null;
}
// only for launch intent.
if (!Intent.ACTION_MAIN.equals(intent.getAction())) {
return null;
}
try {
Bundle bundle = intent.getBundleExtra("_VA_|_sender_");
if (bundle != null) {
IBinder uicallbackToken = BundleCompat.getBinder(bundle, "_VA_|_ui_callback_");
return IUiCallback.Stub.asInterface(uicallbackToken);
}
} catch (Throwable ignored) {
}
return null;
}
public InstalledAppInfo getInstalledAppInfo(String pkg, int flags) {
try {
return getService().getInstalledAppInfo(pkg, flags);
} catch (RemoteException e) {
return VirtualRuntime.crash(e);
}
}
public int getInstalledAppCount() {
try {
return getService().getInstalledAppCount();
} catch (RemoteException e) {
return VirtualRuntime.crash(e);
}
}
public boolean isStartup() {
return isStartUp;
}
public boolean uninstallPackageAsUser(String pkgName, int userId) {
try {
return getService().uninstallPackageAsUser(pkgName, userId);
} catch (RemoteException e) {
// Ignore
}
return false;
}
public boolean uninstallPackage(String pkgName) {
try {
return getService().uninstallPackage(pkgName);
} catch (RemoteException e) {
// Ignore
}
return false;
}
public Resources getResources(String pkg) throws Resources.NotFoundException {
InstalledAppInfo installedAppInfo = getInstalledAppInfo(pkg, 0);
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());
}
throw new Resources.NotFoundException(pkg);
}
public synchronized ActivityInfo resolveActivityInfo(Intent intent, int userId) {
ActivityInfo activityInfo = null;
if (intent.getComponent() == null) {
ResolveInfo resolveInfo = VPackageManager.get().resolveIntent(intent, intent.getType(), 0, userId);
if (resolveInfo != null && resolveInfo.activityInfo != null) {
activityInfo = resolveInfo.activityInfo;
intent.setClassName(activityInfo.packageName, activityInfo.name);
}
} else {
activityInfo = resolveActivityInfo(intent.getComponent(), userId);
}
if (activityInfo != null) {
if (activityInfo.targetActivity != null) {
ComponentName componentName = new ComponentName(activityInfo.packageName, activityInfo.targetActivity);
activityInfo = VPackageManager.get().getActivityInfo(componentName, 0, userId);
intent.setComponent(componentName);
}
}
return activityInfo;
}
public ActivityInfo resolveActivityInfo(ComponentName componentName, int userId) {
return VPackageManager.get().getActivityInfo(componentName, 0, userId);
}
public ServiceInfo resolveServiceInfo(Intent intent, int userId) {
ServiceInfo serviceInfo = null;
ResolveInfo resolveInfo = VPackageManager.get().resolveService(intent, intent.getType(), 0, userId);
if (resolveInfo != null) {
serviceInfo = resolveInfo.serviceInfo;
}
return serviceInfo;
}
public void killApp(String pkg, int userId) {
VActivityManager.get().killAppByPkg(pkg, userId);
}
public void killAllApps() {
VActivityManager.get().killAllApps();
}
public List getInstalledApps(int flags) {
try {
return getService().getInstalledApps(flags);
} catch (RemoteException e) {
return VirtualRuntime.crash(e);
}
}
public List getInstalledAppsAsUser(int userId, int flags) {
try {
return getService().getInstalledAppsAsUser(userId, flags);
} catch (RemoteException e) {
return VirtualRuntime.crash(e);
}
}
public void clearAppRequestListener() {
try {
getService().clearAppRequestListener();
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void scanApps() {
try {
getService().scanApps();
} catch (RemoteException e) {
// Ignore
}
}
public IAppRequestListener getAppRequestListener() {
try {
return getService().getAppRequestListener();
} catch (RemoteException e) {
return VirtualRuntime.crash(e);
}
}
public void setAppRequestListener(final AppRequestListener listener) {
IAppRequestListener inner = new IAppRequestListener.Stub() {
@Override
public void onRequestInstall(final String path) {
VirtualRuntime.getUIHandler().post(new Runnable() {
@Override
public void run() {
listener.onRequestInstall(path);
}
});
}
@Override
public void onRequestUninstall(final String pkg) {
VirtualRuntime.getUIHandler().post(new Runnable() {
@Override
public void run() {
listener.onRequestUninstall(pkg);
}
});
}
};
try {
getService().setAppRequestListener(inner);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public boolean isPackageLaunched(int userId, String packageName) {
try {
return getService().isPackageLaunched(userId, packageName);
} catch (RemoteException e) {
return VirtualRuntime.crash(e);
}
}
public void setPackageHidden(int userId, String packageName, boolean hidden) {
try {
getService().setPackageHidden(userId, packageName, hidden);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public boolean installPackageAsUser(int userId, String packageName) {
try {
return getService().installPackageAsUser(userId, packageName);
} catch (RemoteException e) {
return VirtualRuntime.crash(e);
}
}
public boolean isAppInstalledAsUser(int userId, String packageName) {
try {
return getService().isAppInstalledAsUser(userId, packageName);
} catch (RemoteException e) {
return VirtualRuntime.crash(e);
}
}
public int[] getPackageInstalledUsers(String packageName) {
try {
return getService().getPackageInstalledUsers(packageName);
} catch (RemoteException e) {
return VirtualRuntime.crash(e);
}
}
public abstract static class PackageObserver extends IPackageObserver.Stub {
}
public void registerObserver(IPackageObserver observer) {
try {
getService().registerObserver(observer);
} catch (RemoteException e) {
VirtualRuntime.crash(e);
}
}
public void unregisterObserver(IPackageObserver observer) {
try {
getService().unregisterObserver(observer);
} catch (RemoteException e) {
VirtualRuntime.crash(e);
}
}
public boolean isOutsideInstalled(String packageName) {
try {
return unHookPackageManager.getApplicationInfo(packageName, 0) != null;
} catch (PackageManager.NameNotFoundException e) {
// Ignore
}
return false;
}
public int getSystemPid() {
return systemPid;
}
/**
* Process type
*/
private enum ProcessType {
/**
* Server process
*/
Server,
/**
* Virtual app process
*/
VAppClient,
/**
* Main process
*/
Main,
/**
* Child process
*/
CHILD
}
public interface AppRequestListener {
void onRequestInstall(String path);
void onRequestUninstall(String pkg);
}
public interface OnEmitShortcutListener {
Bitmap getIcon(Bitmap originIcon);
String getName(String originName);
}
public static abstract class VirtualInitializer {
public void onMainProcess() {
}
public void onVirtualProcess() {
}
public void onServerProcess() {
}
public void onChildProcess() {
}
}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/env/Constants.java
================================================
package com.lody.virtual.client.env;
import android.content.Intent;
import com.lody.virtual.client.stub.ShortcutHandleActivity;
import com.lody.virtual.helper.utils.EncodeUtils;
import java.util.Arrays;
import java.util.List;
/**
* @author Lody
*
*/
public class Constants {
public static final String EXTRA_USER_HANDLE = "android.intent.extra.user_handle";
/**
* If an apk declared the "fake-signature" attribute on its Application TAG,
* we will use its signature instead of the real signature.
*
* For more detail, please see :
* https://github.com/microg/android_packages_apps_GmsCore/blob/master/
* patches/android_frameworks_base-M.patch.
*/
public static final String FEATURE_FAKE_SIGNATURE = "fake-signature";
public static final String ACTION_PACKAGE_ADDED = "virtual." + Intent.ACTION_PACKAGE_ADDED;
public static final String ACTION_PACKAGE_REMOVED = "virtual." + Intent.ACTION_PACKAGE_REMOVED;
public static final String ACTION_PACKAGE_CHANGED = "virtual." + Intent.ACTION_PACKAGE_CHANGED;
public static final String ACTION_USER_ADDED = "virtual." + "android.intent.action.USER_ADDED";
public static final String ACTION_USER_REMOVED = "virtual." + "android.intent.action.USER_REMOVED";
public static final String ACTION_USER_INFO_CHANGED = "virtual." + "android.intent.action.USER_CHANGED";
public static final String ACTION_USER_STARTED = "Virtual." + "android.intent.action.USER_STARTED";
public static String META_KEY_IDENTITY = "X-Identity";
public static String META_VALUE_STUB = "Stub-User";
public static String NO_NOTIFICATION_FLAG = ".no_notification";
public static String FAKE_SIGNATURE_FLAG = ".fake_signature";
public static final String WECHAT_PACKAGE = EncodeUtils.decode("Y29tLnRlbmNlbnQubW0="); // wechat
public static final List PRIVILEGE_APP = Arrays.asList(
WECHAT_PACKAGE,
EncodeUtils.decode("Y29tLnRlbmNlbnQubW9iaWxlcXE=")); // qq
/**
* Server process name of VA
*/
public static String SERVER_PROCESS_NAME = ":x";
/**
* The activity who handle the shortcut.
*/
public static String SHORTCUT_PROXY_ACTIVITY_NAME = ShortcutHandleActivity.class.getName();
public static final String PASS_PKG_NAME_ARGUMENT = "MODEL_ARGUMENT";
public static final String PASS_KEY_INTENT = "KEY_INTENT";
public static final String PASS_KEY_USER = "KEY_USER";
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/env/DeadServerException.java
================================================
package com.lody.virtual.client.env;
/**
* @author Lody
*/
public class DeadServerException extends RuntimeException {
public DeadServerException() {
}
public DeadServerException(String message) {
super(message);
}
public DeadServerException(String message, Throwable cause) {
super(message, cause);
}
public DeadServerException(Throwable cause) {
super(cause);
}
public DeadServerException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/env/GPSStateline.java
================================================
package com.lody.virtual.client.env;
class GPSStateline {
private double mAzimuth;
private double mElevation;
private boolean mHasAlmanac;
private boolean mHasEphemeris;
private int mPnr;
private double mSnr;
private boolean mUseInFix;
public double getAzimuth() {
return this.mAzimuth;
}
public double getElevation() {
return this.mElevation;
}
public int getPnr() {
return this.mPnr;
}
public double getSnr() {
return this.mSnr;
}
public boolean isHasAlmanac() {
return this.mHasAlmanac;
}
public boolean isHasEphemeris() {
return this.mHasEphemeris;
}
public boolean isUseInFix() {
return this.mUseInFix;
}
public void setAzimuth(double azimuth) {
this.mAzimuth = azimuth;
}
public void setElevation(double elevation) {
this.mElevation = elevation;
}
public void setHasAlmanac(boolean hasAlmanac) {
this.mHasAlmanac = hasAlmanac;
}
public void setHasEphemeris(boolean hasEphemeris) {
this.mHasEphemeris = hasEphemeris;
}
public void setPnr(int pnr) {
this.mPnr = pnr;
}
public void setSnr(double snr) {
this.mSnr = snr;
}
public void setUseInFix(boolean useInFix) {
this.mUseInFix = useInFix;
}
public GPSStateline(int pnr, double snr, double elevation, double azimuth, boolean useInFix, boolean hasAlmanac, boolean hasEphemeris) {
this.mPnr = pnr;
this.mSnr = snr;
this.mElevation = elevation;
this.mAzimuth = azimuth;
this.mUseInFix = useInFix;
this.mHasAlmanac = hasAlmanac;
this.mHasEphemeris = hasEphemeris;
}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/env/SpecialComponentList.java
================================================
package com.lody.virtual.client.env;
import android.Manifest;
import android.app.DownloadManager;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import mirror.android.webkit.IWebViewUpdateService;
import mirror.android.webkit.WebViewFactory;
/**
* @author Lody
*/
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 Set SYSTEM_BROADCAST_ACTION = new HashSet<>(7);
private static String PROTECT_ACTION_PREFIX = "_VA_protected_";
static {
SYSTEM_BROADCAST_ACTION.add(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_SCREEN_ON);
SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_SCREEN_OFF);
SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_NEW_OUTGOING_CALL);
SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_TIME_TICK);
SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_TIME_CHANGED);
SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_TIMEZONE_CHANGED);
SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_BATTERY_CHANGED);
SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_BATTERY_LOW);
SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_BATTERY_OKAY);
SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_POWER_CONNECTED);
SYSTEM_BROADCAST_ACTION.add(Intent.ACTION_POWER_DISCONNECTED);
SYSTEM_BROADCAST_ACTION.add("android.provider.Telephony.SMS_RECEIVED");
SYSTEM_BROADCAST_ACTION.add("android.provider.Telephony.SMS_DELIVER");
SYSTEM_BROADCAST_ACTION.add("android.net.wifi.STATE_CHANGE");
SYSTEM_BROADCAST_ACTION.add("android.net.wifi.SCAN_RESULTS");
SYSTEM_BROADCAST_ACTION.add("android.net.wifi.WIFI_STATE_CHANGED");
SYSTEM_BROADCAST_ACTION.add("android.net.conn.CONNECTIVITY_CHANGE");
SYSTEM_BROADCAST_ACTION.add("android.intent.action.ANY_DATA_STATE");
SYSTEM_BROADCAST_ACTION.add("android.intent.action.SIM_STATE_CHANGED");
SYSTEM_BROADCAST_ACTION.add("android.location.PROVIDERS_CHANGED");
SYSTEM_BROADCAST_ACTION.add("android.location.MODE_CHANGED");
ACTION_BLACK_LIST.add("android.appwidget.action.APPWIDGET_UPDATE");
WHITE_PERMISSION.add("com.google.android.gms.settings.SECURITY_SETTINGS");
WHITE_PERMISSION.add("com.google.android.apps.plus.PRIVACY_SETTINGS");
WHITE_PERMISSION.add(Manifest.permission.ACCOUNT_MANAGER);
PROTECTED_ACTION_MAP.put(Intent.ACTION_PACKAGE_ADDED, Constants.ACTION_PACKAGE_ADDED);
PROTECTED_ACTION_MAP.put(Intent.ACTION_PACKAGE_REMOVED, Constants.ACTION_PACKAGE_REMOVED);
PROTECTED_ACTION_MAP.put(Intent.ACTION_PACKAGE_CHANGED, Constants.ACTION_PACKAGE_CHANGED);
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);
}
/**
* Check if the action in the BlackList.
*
* @param action Action
*/
public static boolean isActionInBlackList(String action) {
return ACTION_BLACK_LIST.contains(action);
}
/**
* Add an action to the BlackList.
*
* @param action action
*/
public static void addBlackAction(String action) {
ACTION_BLACK_LIST.add(action);
}
public static void protectIntentFilter(IntentFilter filter) {
if (filter != null) {
List actions = mirror.android.content.IntentFilter.mActions.get(filter);
ListIterator iterator = actions.listIterator();
while (iterator.hasNext()) {
String action = iterator.next();
if (SpecialComponentList.isActionInBlackList(action)) {
iterator.remove();
continue;
}
if (SYSTEM_BROADCAST_ACTION.contains(action)) {
continue;
}
String newAction = SpecialComponentList.protectAction(action);
if (newAction != null) {
iterator.set(newAction);
}
}
}
}
public static void protectIntent(Intent intent) {
String protectAction = protectAction(intent.getAction());
if (protectAction != null) {
intent.setAction(protectAction);
}
}
public static void unprotectIntent(Intent intent) {
String unprotectAction = unprotectAction(intent.getAction());
if (unprotectAction != null) {
intent.setAction(unprotectAction);
}
}
public static String protectAction(String originAction) {
if (originAction == null) {
return null;
}
if (originAction.startsWith("_VA_")) {
return originAction;
}
String newAction = PROTECTED_ACTION_MAP.get(originAction);
if (newAction == null) {
newAction = PROTECT_ACTION_PREFIX + originAction;
}
return newAction;
}
public static String unprotectAction(String action) {
if (action == null) {
return null;
}
if (action.startsWith(PROTECT_ACTION_PREFIX)) {
return action.substring(PROTECT_ACTION_PREFIX.length());
}
for (Map.Entry next : PROTECTED_ACTION_MAP.entrySet()) {
String modifiedAction = next.getValue();
if (modifiedAction.equals(action)) {
return next.getKey();
}
}
return null;
}
public static boolean isWhitePermission(String permission) {
return WHITE_PERMISSION.contains(permission);
}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/env/VirtualGPSSatalines.java
================================================
package com.lody.virtual.client.env;
import java.util.ArrayList;
import java.util.List;
public class VirtualGPSSatalines {
private static VirtualGPSSatalines INSTANCE;
private int mAlmanacMask;
private float[] mAzimuths;
private float[] mElevations;
private int mEphemerisMask;
private float[] mSnrs;
private int mUsedInFixMask;
private int[] pnrs;
private int[] prnWithFlags;
private int svCount;
static {
INSTANCE = new VirtualGPSSatalines();
}
public int getAlmanacMask() {
return this.mAlmanacMask;
}
public float[] getAzimuths() {
return this.mAzimuths;
}
public float[] getElevations() {
return this.mElevations;
}
public int getEphemerisMask() {
return this.mEphemerisMask;
}
public int[] getPrns() {
return this.pnrs;
}
public float[] getSnrs() {
return this.mSnrs;
}
public int getUsedInFixMask() {
return this.mUsedInFixMask;
}
public static VirtualGPSSatalines get() {
return INSTANCE;
}
private VirtualGPSSatalines() {
List statelines = new ArrayList<>();
statelines.add(new GPSStateline(5, 1.0d, 5.0d, 112.0d, false, true, true));
statelines.add(new GPSStateline(13, 13.5d, 23.0d, 53.0d, true, true, true));
statelines.add(new GPSStateline(14, 19.1d, 6.0d, 247.0d, true, true, true));
statelines.add(new GPSStateline(15, 31.0d, 58.0d, 45.0d, true, true, true));
statelines.add(new GPSStateline(18, 0.0d, 52.0d, 309.0d, false, true, true));
statelines.add(new GPSStateline(20, 30.1d, 54.0d, 105.0d, true, true, true));
statelines.add(new GPSStateline(21, 33.2d, 56.0d, 251.0d, true, true, true));
statelines.add(new GPSStateline(22, 0.0d, 14.0d, 299.0d, false, true, true));
statelines.add(new GPSStateline(24, 25.9d, 57.0d, 157.0d, true, true, true));
statelines.add(new GPSStateline(27, 18.0d, 3.0d, 309.0d, true, true, true));
statelines.add(new GPSStateline(28, 18.2d, 3.0d, 42.0d, true, true, true));
statelines.add(new GPSStateline(41, 28.8d, 0.0d, 0.0d, false, false, false));
statelines.add(new GPSStateline(50, 29.2d, 0.0d, 0.0d, false, true, true));
statelines.add(new GPSStateline(67, 14.4d, 2.0d, 92.0d, false, false, false));
statelines.add(new GPSStateline(68, 21.2d, 45.0d, 60.0d, false, false, false));
statelines.add(new GPSStateline(69, 17.5d, 50.0d, 330.0d, false, true, true));
statelines.add(new GPSStateline(70, 22.4d, 7.0d, 291.0d, false, false, false));
statelines.add(new GPSStateline(77, 23.8d, 10.0d, 23.0d, true, true, true));
statelines.add(new GPSStateline(78, 18.0d, 47.0d, 70.0d, true, true, true));
statelines.add(new GPSStateline(79, 22.8d, 41.0d, 142.0d, true, true, true));
statelines.add(new GPSStateline(83, 0.2d, 9.0d, 212.0d, false, false, false));
statelines.add(new GPSStateline(84, 16.7d, 30.0d, 264.0d, true, true, true));
statelines.add(new GPSStateline(85, 12.1d, 20.0d, 317.0d, true, true, true));
this.svCount = statelines.size();
this.pnrs = new int[statelines.size()];
for (int i = 0; i < statelines.size(); i++) {
this.pnrs[i] = statelines.get(i).getPnr();
}
this.mSnrs = new float[statelines.size()];
for (int i = 0; i < statelines.size(); i++) {
this.mSnrs[i] = (float) statelines.get(i).getSnr();
}
this.mElevations = new float[statelines.size()];
for (int i = 0; i < statelines.size(); i++) {
this.mElevations[i] = (float) statelines.get(i).getElevation();
}
this.mAzimuths = new float[statelines.size()];
for (int i = 0; i < statelines.size(); i++) {
this.mAzimuths[i] = (float) statelines.get(i).getAzimuth();
}
this.mEphemerisMask = 0;
for (int i = 0; i < statelines.size(); i++) {
if (statelines.get(i).isHasEphemeris()) {
this.mEphemerisMask |= 1 << (statelines.get(i).getPnr() - 1);
}
}
this.mAlmanacMask = 0;
for (int i = 0; i < statelines.size(); i++) {
if (statelines.get(i).isHasAlmanac()) {
this.mAlmanacMask |= 1 << (statelines.get(i).getPnr() - 1);
}
}
this.mUsedInFixMask = 0;
for (int i = 0; statelines.size() > i; i++) {
if (statelines.get(i).isUseInFix()) {
this.mUsedInFixMask |= 1 << (statelines.get(i).getPnr() - 1);
}
}
this.prnWithFlags = new int[statelines.size()];
for (int i = 0; i < statelines.size(); i++) {
GPSStateline gpsStateline = statelines.get(i);
this.prnWithFlags[i] =
(gpsStateline.isHasEphemeris() ? 1 : 0)
| (gpsStateline.isHasAlmanac() ? 1 : 0) << 1
| (gpsStateline.isUseInFix() ? 1 : 0) << 2
| 8
| (gpsStateline.getPnr() << 7);
}
}
public int getSvCount() {
return this.svCount;
}
public int[] getPrnWithFlags() {
return this.prnWithFlags;
}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/env/VirtualRuntime.java
================================================
package com.lody.virtual.client.env;
import android.content.pm.ApplicationInfo;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
import com.lody.virtual.client.core.VirtualCore;
import com.lody.virtual.helper.utils.VLog;
import mirror.android.ddm.DdmHandleAppName;
import mirror.android.ddm.DdmHandleAppNameJBMR1;
/**
* @author Lody
*
*
* Runtime Environment for App.
*/
public class VirtualRuntime {
private static final Handler sUIHandler = new Handler(Looper.getMainLooper());
private static String sInitialPackageName;
private static String sProcessName;
public static Handler getUIHandler() {
return sUIHandler;
}
public static String getProcessName() {
return sProcessName;
}
public static String getInitialPackageName() {
return sInitialPackageName;
}
public static void setupRuntime(String processName, ApplicationInfo appInfo) {
if (sProcessName != null) {
return;
}
sInitialPackageName = appInfo.packageName;
sProcessName = processName;
mirror.android.os.Process.setArgV0.call(processName);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
DdmHandleAppNameJBMR1.setAppName.call(processName, 0);
} else {
DdmHandleAppName.setAppName.call(processName);
}
}
public static T crash(RemoteException e) throws RuntimeException {
e.printStackTrace();
if (VirtualCore.get().isVAppProcess()) {
Process.killProcess(Process.myPid());
System.exit(0);
}
throw new DeadServerException(e);
}
public static boolean isArt() {
return System.getProperty("java.vm.version").startsWith("2");
}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/fixer/ActivityFixer.java
================================================
package com.lody.virtual.client.fixer;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.WallpaperManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import mirror.com.android.internal.R_Hide;
/**
* @author Lody
*
*/
public final class ActivityFixer {
private ActivityFixer() {
}
public static void fixActivity(Activity activity) {
Context baseContext = activity.getBaseContext();
try {
TypedArray typedArray = activity.obtainStyledAttributes((R_Hide.styleable.Window.get()));
if (typedArray != null) {
boolean showWallpaper = typedArray.getBoolean(R_Hide.styleable.Window_windowShowWallpaper.get(),
false);
if (showWallpaper) {
activity.getWindow().setBackgroundDrawable(WallpaperManager.getInstance(activity).getDrawable());
}
typedArray.recycle();
}
} catch (Throwable e) {
e.printStackTrace();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Intent intent = activity.getIntent();
ApplicationInfo applicationInfo = baseContext.getApplicationInfo();
PackageManager pm = activity.getPackageManager();
if (intent != null && activity.isTaskRoot()) {
try {
String label = applicationInfo.loadLabel(pm) + "";
Bitmap icon = null;
Drawable drawable = applicationInfo.loadIcon(pm);
if (drawable instanceof BitmapDrawable) {
icon = ((BitmapDrawable) drawable).getBitmap();
}
activity.setTaskDescription(new ActivityManager.TaskDescription(label, icon));
} catch (Throwable e) {
e.printStackTrace();
}
}
}
}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/fixer/ComponentFixer.java
================================================
package com.lody.virtual.client.fixer;
import android.content.pm.ComponentInfo;
import android.text.TextUtils;
import com.lody.virtual.server.pm.PackageSetting;
/**
* @author Lody
*/
public class ComponentFixer {
public static String fixComponentClassName(String pkgName, String className) {
if (className != null) {
if (className.charAt(0) == '.') {
return pkgName + className;
}
return className;
}
return null;
}
public static void fixComponentInfo(PackageSetting setting, ComponentInfo info, int userId) {
if (info != null) {
if (TextUtils.isEmpty(info.processName)) {
info.processName = info.packageName;
}
info.name = fixComponentClassName(info.packageName, info.name);
if (info.processName == null) {
info.processName = info.applicationInfo.processName;
}
}
}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/fixer/ContextFixer.java
================================================
package com.lody.virtual.client.fixer;
import android.content.Context;
import android.content.ContextWrapper;
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;
import com.lody.virtual.client.hook.proxies.dropbox.DropBoxManagerStub;
import com.lody.virtual.client.hook.proxies.graphics.GraphicsStatsStub;
import com.lody.virtual.helper.utils.Reflect;
import com.lody.virtual.helper.utils.ReflectException;
import mirror.android.app.ContextImpl;
import mirror.android.app.ContextImplKitkat;
import mirror.android.content.ContentResolverJBMR2;
/**
* @author Lody
*/
public class ContextFixer {
private static final String TAG = ContextFixer.class.getSimpleName();
/**
* Fuck AppOps
*
* @param context Context
*/
public static void fixContext(Context context) {
try {
context.getPackageName();
} catch (Throwable e) {
return;
}
InvocationStubManager.getInstance().checkEnv(GraphicsStatsStub.class);
int deep = 0;
while (context instanceof ContextWrapper) {
context = ((ContextWrapper) context).getBaseContext();
deep++;
if (deep >= 10) {
return;
}
}
ContextImpl.mPackageManager.set(context, null);
try {
context.getPackageManager();
} catch (Throwable e) {
e.printStackTrace();
}
if (!VirtualCore.get().isVAppProcess()) {
return;
}
DropBoxManager dm = (DropBoxManager) context.getSystemService(Context.DROPBOX_SERVICE);
BinderInvocationStub boxBinder = InvocationStubManager.getInstance().getInvocationStub(DropBoxManagerStub.class);
if (boxBinder != null) {
try {
Reflect.on(dm).set("mService", boxBinder.getProxyInterface());
} catch (ReflectException e) {
e.printStackTrace();
}
}
String hostPkg = VirtualCore.get().getHostPkg();
ContextImpl.mBasePackageName.set(context, hostPkg);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
ContextImplKitkat.mOpPackageName.set(context, hostPkg);
}
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();
}
}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/base/BinderInvocationProxy.java
================================================
package com.lody.virtual.client.hook.base;
import android.os.IBinder;
import android.os.IInterface;
import mirror.RefStaticMethod;
import mirror.android.os.ServiceManager;
/**
* @author Paulo Costa
*
* @see MethodInvocationProxy
*/
public abstract class BinderInvocationProxy extends MethodInvocationProxy {
protected String mServiceName;
public BinderInvocationProxy(IInterface stub, String serviceName) {
this(new BinderInvocationStub(stub), serviceName);
}
public BinderInvocationProxy(RefStaticMethod asInterfaceMethod, String serviceName) {
this(new BinderInvocationStub(asInterfaceMethod, ServiceManager.getService.call(serviceName)), serviceName);
}
public BinderInvocationProxy(Class> stubClass, String serviceName) {
this(new BinderInvocationStub(stubClass, ServiceManager.getService.call(serviceName)), serviceName);
}
public BinderInvocationProxy(BinderInvocationStub hookDelegate, String serviceName) {
super(hookDelegate);
this.mServiceName = serviceName;
}
@Override
public void inject() throws Throwable {
getInvocationStub().replaceService(mServiceName);
}
@Override
public boolean isEnvBad() {
IBinder binder = ServiceManager.getService.call(mServiceName);
return binder != null && getInvocationStub() != binder;
}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/base/BinderInvocationStub.java
================================================
package com.lody.virtual.client.hook.base;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;
import com.lody.virtual.client.core.VirtualCore;
import java.io.FileDescriptor;
import java.lang.reflect.Method;
import mirror.RefStaticMethod;
import mirror.android.os.ServiceManager;
/**
* @author Lody
*/
@SuppressWarnings("unchecked")
public class BinderInvocationStub extends MethodInvocationStub implements IBinder {
private static final String TAG = BinderInvocationStub.class.getSimpleName();
private IBinder mBaseBinder;
public BinderInvocationStub(RefStaticMethod asInterfaceMethod, IBinder binder) {
this(asInterface(asInterfaceMethod, binder));
}
public BinderInvocationStub(Class> stubClass, IBinder binder) {
this(asInterface(stubClass, binder));
}
public BinderInvocationStub(IInterface mBaseInterface) {
super(mBaseInterface);
mBaseBinder = getBaseInterface() != null ? getBaseInterface().asBinder() : null;
addMethodProxy(new AsBinder());
}
private static IInterface asInterface(RefStaticMethod asInterfaceMethod, IBinder binder) {
if (asInterfaceMethod == null || binder == null) {
return null;
}
return asInterfaceMethod.call(binder);
}
private static IInterface asInterface(Class> stubClass, IBinder binder) {
try {
if (stubClass == null || binder == null) {
return null;
}
Method asInterface = stubClass.getMethod("asInterface", IBinder.class);
return (IInterface) asInterface.invoke(null, binder);
} catch (Exception e) {
Log.d(TAG, "Could not create stub " + stubClass.getName() + ". Cause: " + e);
return null;
}
}
public void replaceService(String name) {
if (mBaseBinder != null) {
ServiceManager.sCache.get().put(name, this);
}
}
private final class AsBinder extends MethodProxy {
@Override
public String getMethodName() {
return "asBinder";
}
@Override
public Object call(Object who, Method method, Object... args) throws Throwable {
return BinderInvocationStub.this;
}
}
@Override
public String getInterfaceDescriptor() throws RemoteException {
return mBaseBinder.getInterfaceDescriptor();
}
public Context getContext() {
return VirtualCore.get().getContext();
}
@Override
public boolean pingBinder() {
return mBaseBinder.pingBinder();
}
@Override
public boolean isBinderAlive() {
return mBaseBinder.isBinderAlive();
}
@Override
public IInterface queryLocalInterface(String descriptor) {
return getProxyInterface();
}
@Override
public void dump(FileDescriptor fd, String[] args) throws RemoteException {
mBaseBinder.dump(fd, args);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
@Override
public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException {
mBaseBinder.dumpAsync(fd, args);
}
@Override
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
return mBaseBinder.transact(code, data, reply, flags);
}
@Override
public void linkToDeath(DeathRecipient recipient, int flags) throws RemoteException {
mBaseBinder.linkToDeath(recipient, flags);
}
@Override
public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
return mBaseBinder.unlinkToDeath(recipient, flags);
}
public IBinder getBaseBinder() {
return mBaseBinder;
}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/base/Inject.java
================================================
package com.lody.virtual.client.hook.base;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Lody
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Inject {
Class> value();
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/base/LogInvocation.java
================================================
package com.lody.virtual.client.hook.base;
import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Add this annotation to a {@link MethodProxy} or a {@link MethodInvocationStub} to
* log all the calls and their arguments.
*
* Obviously, this is only useful for debugging.
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface LogInvocation {
public Condition value() default Condition.ALWAYS;
static enum Condition {
/** Never logs anything */
NEVER {
public int getLogLevel(boolean isHooked, boolean isError) {
return -1;
}
},
/**
* Logs every call.
* Mostly useful for debugging.
*/
ALWAYS {
public int getLogLevel(boolean isHooked, boolean isError) {
return isError ? Log.WARN : Log.INFO;
}
},
/**
* Logs only calls that exited with error
* A reasonable tradeoff between noise and getting relevant information
*/
ON_ERROR {
public int getLogLevel(boolean isHooked, boolean isError) {
return isError ? Log.WARN : -1;
}
},
/**
* Log only calls that haven't been hooked
* It only makes sense on a MethodInvocationProxy, and is useful to pinpoint missing methods
*/
NOT_HOOKED {
public int getLogLevel(boolean isHooked, boolean isError) {
return isHooked ? -1 : isError ? Log.WARN : Log.INFO;
}
};
public abstract int getLogLevel(boolean isHooked, boolean isError);
};
};
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/base/MethodBox.java
================================================
package com.lody.virtual.client.hook.base;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author Lody
*/
@SuppressWarnings("unchecked")
public class MethodBox {
public final Method method;
public final Object who;
public final Object[] args;
public MethodBox(Method method, Object who, Object[] args) {
this.method = method;
this.who = who;
this.args = args;
}
public T call() throws InvocationTargetException {
try {
return (T) method.invoke(who, args);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public T callSafe() {
try {
return (T) method.invoke(who, args);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/base/MethodInvocationProxy.java
================================================
package com.lody.virtual.client.hook.base;
import android.content.Context;
import com.lody.virtual.client.core.InvocationStubManager;
import com.lody.virtual.client.core.VirtualCore;
import com.lody.virtual.client.interfaces.IInjector;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
/**
* @author Lody
*
* This class is responsible with:
* - Instantiating a {@link MethodInvocationStub.HookInvocationHandler} on {@link #getInvocationStub()} ()}
* - Install a bunch of {@link MethodProxy}s, either with a @{@link Inject} annotation or manually
* calling {@link #addMethodProxy(MethodProxy)} from {@link #onBindMethods()}
* - Install the hooked object on the Runtime via {@link #inject()}
*
* All {@link MethodInvocationProxy}s (plus a couple of other @{@link IInjector}s are installed by
* {@link InvocationStubManager}
* @see Inject
*/
public abstract class MethodInvocationProxy implements IInjector {
protected T mInvocationStub;
public MethodInvocationProxy(T invocationStub) {
this.mInvocationStub = invocationStub;
onBindMethods();
afterHookApply(invocationStub);
LogInvocation loggingAnnotation = getClass().getAnnotation(LogInvocation.class);
if (loggingAnnotation != null) {
invocationStub.setInvocationLoggingCondition(loggingAnnotation.value());
}
}
protected void onBindMethods() {
if (mInvocationStub == null) {
return;
}
Class extends MethodInvocationProxy> clazz = getClass();
Inject inject = clazz.getAnnotation(Inject.class);
if (inject != null) {
Class> proxiesClass = inject.value();
Class>[] innerClasses = proxiesClass.getDeclaredClasses();
for (Class> innerClass : innerClasses) {
if (!Modifier.isAbstract(innerClass.getModifiers())
&& MethodProxy.class.isAssignableFrom(innerClass)
&& innerClass.getAnnotation(SkipInject.class) == null) {
addMethodProxy(innerClass);
}
}
}
}
private void addMethodProxy(Class> hookType) {
try {
Constructor> constructor = hookType.getDeclaredConstructors()[0];
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
MethodProxy methodProxy;
if (constructor.getParameterTypes().length == 0) {
methodProxy = (MethodProxy) constructor.newInstance();
} else {
methodProxy = (MethodProxy) constructor.newInstance(this);
}
mInvocationStub.addMethodProxy(methodProxy);
} catch (Throwable e) {
throw new RuntimeException("Unable to instance Hook : " + hookType + " : " + e.getMessage());
}
}
public MethodProxy addMethodProxy(MethodProxy methodProxy) {
return mInvocationStub.addMethodProxy(methodProxy);
}
protected void afterHookApply(T delegate) {
}
@Override
public abstract void inject() throws Throwable;
public Context getContext() {
return VirtualCore.get().getContext();
}
public T getInvocationStub() {
return mInvocationStub;
}
}
================================================
FILE: VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/base/MethodInvocationStub.java
================================================
package com.lody.virtual.client.hook.base;
import android.text.TextUtils;
import android.util.Log;
import com.lody.virtual.client.hook.utils.MethodParameterUtils;
import com.lody.virtual.helper.utils.VLog;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* @author Lody
*
* HookHandler uses Java's {@link Proxy} to create a wrapper for existing services.
*
* When any method is called on the wrapper, it checks if there is any {@link MethodProxy} registered
* and enabled for that method. If so, it calls the startUniformer instead of the wrapped implementation.
*