Cloning Any App Into Samsung's Dual App Profile
Samsung's Dual Messenger only offers to clone WhatsApp and Facebook. The underlying profile is generic, one ADB command clones anything into it, and one more stops Samsung from undoing it on reboot.
Note
Package names, device model, and Android version in this post are real (Galaxy S24 FE, One UI 8.5 on Android 16). I’ve redacted local network addresses since they aren’t relevant to the technique
at first this had nothing to do with dual apps at all. It started with trying to share a couple of Windows folders with my phone over SMB, tightened down to that one device instead of the whole Wi-Fi network. That went fine, but it left me wanting proper cloud storage access from Android instead of ad-hoc file shares, so I went looking for a trustworthy rclone/S3 client. Closed-source options were an easy no, so I picked two open-source ones, RSAF ↗ and a maintained fork of Round-Sync, and decided to build both from source rather than sideload someone else’s binary.
RSAF never built, its toolchain is pinned to an Android SDK version so new that
even the maintainer’s own CI has to pin the emulator to a lower API “until the
action supports it.” Round-Sync did build, after fixing a missing GOOS=android
in its dependency resolution and a 16KB page-alignment issue in its native
library. None of that is this post. This post is about a side effect I noticed
while reinstalling Round-Sync over and over during that debugging: two identical
app icons
Two icons, one APK#
I’d run adb install -r app-debug.apk, and the launcher would show a duplicate,
same name, same icon, one with a small badge overlay. My first guess was Secure
App, Samsung’s Knox-isolated profile. Nah, it was wrong, the badge didn’t look like
Secure Folder’s lock icon, and a permission error from pm list packages alone
doesn’t distinguish between Samsung’s various profile types anyway. Also, the Secure App only apear in Secure Folder, not on home screen. So I checked
properly:
$ adb shell pm list users
Users:
UserInfo{0:null:4c13} running
UserInfo{95:DUAL_APP:20001010} running
UserInfo{150:Secure Folder:61010} runningplaintextThree profiles. 150 is Secure Folder, confirmed separately. 95 is named
DUAL_APP, and a package check confirmed it:
$ adb shell pm list packages --user 95 | grep felixnuesse
package:de.felixnuesse.extract.debugplaintextthe app showed up on both the main profile and this dual-app profile, though I’d never opened Samsung’s dual-app settings for it
Important
Settings > Advanced features > Dual Messenger on this device lists exactly two
apps: WhatsApp and Facebook. There is no toggle for anything else. And yet an
unrelated file-manager app was sitting in that profile.
Ruling out the obvious explanation#
My first guess was that Dual Messenger secretly supports more than it lets on, or maybe some Good Lock module got switched on somewhere along the way. Neither one held up though:
- The Dual Messenger app list has exactly those two apps, no hidden “add more” screen.
- Apps installed the normal way, tapping a downloaded APK through the system installer, never get cloned. A local test with an unrelated open-source app confirmed this: install by tapping the file, no duplicate appears.
The only apps that ended up duplicated were the ones I had installed via
adb install in the same session. That’s the actual variable. The dual-app
setting had nothing to do with it.
$ adb shell dumpsys user
UserInfo{95:xxx:20001010} serialNo=95 isPrimary=false parentId=0
Type: android.os.usertype.profile.CLONE
Flags: 536875024 (DUALAPP_PROFILE|INITIALIZED|PROFILE)
Created: +537d20h20m49s210ms agoplaintextandroid.os.usertype.profile.CLONE is AOSP’s generic clone-profile primitive.
Samsung didn’t invent it. Dual Messenger is one front-end that writes into it,
with a curated app allowlist baked into its own UI. The profile itself has no
such allowlist, it’s a plain Android user, and pm doesn’t care which front-end
you’d normally use to populate it.
The actual technique#
If the profile is generic, installing into it directly should work without going anywhere near Dual Messenger’s UI at all. Android’s package manager has a command built for exactly this: cloning an app that’s already installed into another user, without touching its APK.
$ adb shell pm list packages --user 0 | grep discord
package:com.discord
$ adb shell pm install-existing --user 95 com.discord
Package com.discord installed for user: 95
$ adb shell pm list packages --user 95 | grep discord
package:com.discordplaintextThat’s the whole trick: no APK re-download, no re-signing. It’s the same
installed package already verified and running on your main profile, registered
against a second user, so there’s no risk of ending up with a different build.
The new icon appears in the launcher, with its own isolated /data/user/95/
storage: a second, independent login.
flowchart LR
subgraph Device
M[Main profile<br/>user 0]
D[Dual App profile<br/>user 95, CLONE type]
S[Secure Folder<br/>user 150]
end
UI[Dual Messenger UI] -->|writes only<br/>WhatsApp/Facebook| D
ADB["pm install-existing --user 95 <pkg>"] -->|writes any package| D
style ADB fill:#22c55e,color:#000
style UI fill:#6b7280,color:#fff
The Dual Messenger UI and the raw pm command both write into the same profile.
One gates it behind an allowlist, the other doesn’t gate it at all.
Tip
If the app isn’t installed on the main profile yet, pm install-existing has
nothing to clone. Install it normally first (Play Store or a verified APK), then
run the command above.
Then it disappeared#
I wrote the first draft of this post, logged into the cloned Discord, and moved on. Some time later the second icon was gone. The app and its data were gone too:
$ adb shell dumpsys package com.discord | grep "User 95"
User 95: ceDataInode=-1 deDataInode=-1 installed=false ...plaintextceDataInode=-1 means the profile’s copy and its data were deleted. Same for
the two other apps I’d cloned. Profile 95 was back to exactly one third-party
package: WhatsApp.
Android’s log buffer only holds a few hours, so whatever did it had scrolled away. I re-cloned Discord and started watching. Nothing happened for fourteen minutes of normal use. Then I rebooted the phone, and two minutes after boot:
18:13:19.385 E DualAppManagerService: getAllWhitelistedPackages : empty
18:13:19.385 E SemDualAppManager: getAllWhitelistedPackages : null returned. Return default
18:13:19.395 I PackageManager: START INSTALL-EXISTING PACKAGE: userId{95} pkg{com.samsung.android.permissioncontroller}
18:13:19.395 I PackageManager: Request from pId{12519 / com.samsung.android.da.daagent}
18:13:19.447 I PackageManager: START DELETE PACKAGE: observer{138464497}
18:13:19.447 I PackageManager: pkg{com.discord}, user{95}, caller{1000} flags{0}
18:13:19.451 I ActivityManager: Force stopping com.discord appid=10378 user=95: deletePackageXplaintextThat’s the whole mechanism in seven lines. com.samsung.android.da.daagent,
the Dual Messenger agent, runs a reconciliation pass on every boot:
- Ask the system service for the allowlist. It comes back empty, so it falls back to the built-in default (WhatsApp and Facebook).
install-existingits own helper packages into the profile, using the exact same command I’d used, which is a nice confirmation that this is the intended way to populate it.- Delete every package in the profile that isn’t on the list. Data included.
The profile is generic, the restriction lives in a separate app, and that separate app is also a janitor that shows up after every reboot. Nothing in the fourteen minutes of runtime triggered it, only the boot did.
sequenceDiagram
participant Boot
participant DA as daagent
participant PM as PackageManager
participant P95 as Profile 95
Boot->>DA: start
DA->>PM: getAllWhitelistedPackages()
PM-->>DA: empty → default [WhatsApp, Facebook]
DA->>PM: install-existing helpers → user 95
DA->>PM: for pkg in profile 95 not in list: deletePackage(pkg, 95)
PM->>P95: remove com.discord + data
I looked for somewhere to write to that allowlist. dumpsys dual_app prints
nothing, there are no settings keys for it, and daagent’s data directory is
off-limits without root. Samsung either baked the list into the APK or feeds it
from their servers. Either way, it isn’t ours to edit.
Making it stick#
If the allowlist can’t be changed, the other lever is the thing that enforces it. daagent is an ordinary app, and ordinary apps can be disabled per user:
$ adb shell pm install-existing --user 95 com.discord
Package com.discord installed for user: 95
$ adb shell pm disable-user --user 0 com.samsung.android.da.daagent
Package com.samsung.android.da.daagent new state: disabled-userplaintextThen reboot. Two minutes after boot, no START DELETE PACKAGE, no daagent
process, and both clones still there and working, including the WhatsApp one
that Samsung set up itself.
Important
Disabling daagent doesn’t uninstall anything or touch the profile’s data, it only stops the app that would delete things. In the “will my WhatsApp clone get wiped” sense, this makes the profile safer, not riskier. The one thing that can still wipe it is turning Dual Messenger off in Settings, which removes the whole profile, and that’s true with or without this trick.
Disabling daagent costs you a few things (from what i researched on the internet): the Dual Messenger settings page probably won’t work, and clone notifications might be less reliable since daagent handles some of the profile’s plumbing. The badge overlay on clone icons may go away too. All of it comes back with the mirror command:
$ adb shell pm enable com.samsung.android.da.daagentplaintextwhich also means the next reboot will sweep Discord out again.
Tip
Back up the WhatsApp clone (Settings → Chats → Chat backup, from inside the clone) before doing any of this. The commands above don’t threaten it. A clone profile with no backup is one accidental Settings toggle away from gone regardless.
If you’d rather not touch a system app at all, the fallback is to re-run
install-existing after each reboot and log in again. Safe, but annoying.
What’s still a guess#
One thing I never nailed down is why plain adb install (no --user flag) put
apps into the dual-app profile in the first place, which is what started all of
this. The working theory is that adb install on this OEM build targets all
users while the tap-to-install path targets only the current one, but I don’t
have a controlled test to call that proven. Everything in the two sections
above, the reboot sweep and the fix for it, is verified from the logs and
reproducible on demand.
Caveats before you do this#
-
Permission prompts don’t stick. The cloned Discord asked for photo access every time and never got it.
dumpsys packageshowed why: every runtime permission for user 95 wasgranted=falsewith noUSER_SETflag, so my “Allow” taps were never recorded, andPOST_NOTIFICATIONSwas denied the same way. Grant them from the shell instead:
plaintext$ adb shell pm grant --user 95 com.discord android.permission.READ_MEDIA_IMAGES $ adb shell pm grant --user 95 com.discord android.permission.POST_NOTIFICATIONSSame for
READ_MEDIA_VIDEO,CAMERA,RECORD_AUDIO, whatever the app asks for. Force-close the clone afterwards. -
Notifications can still be less reliable on a clone-profile instance of an app that was never designed with Samsung’s dual-app framework in mind, even once the permission is granted. Log in and send yourself a test message before relying on it.
-
This isn’t an officially supported configuration for arbitrary apps, only Dual Messenger’s own allowlisted apps get Samsung’s testing and support.
-
Check the app’s terms of service if the clone is for running a second account. Some services restrict multi-accounting, and this post is about the Android mechanism, not a statement on any particular app’s policy.
-
Removing a clone is the mirror command:
adb shell pm uninstall --user 95 <package>.
Takeaways#
- A restrictive front-end UI doesn’t mean the underlying platform mechanism is restrictive too. Samsung’s Dual Messenger allowlist lives in its own app. The clone-profile primitive it writes to has none.
pm install-existing --user <id> <package>clones an already-installed app into any user profile without touching the APK. Samsung’s own agent uses the same command.- If something you set up reverts on its own, suspect a boot-time job before
suspecting a bug.
logcatright after a reboot, filtered on the package name, found the culprit in seconds. pm disable-useron the enforcing app is a legitimate, reversible fix when the policy it enforces isn’t editable. Know what else that app does before you switch it off.