Intro/caveats

I tested these instructions using GrapheneOS 15 (2024120900) and an account issued by my work, which uses Google's "advanced" mobile management level. This level prompted me to install the Google Apps Device Policy app. If your work uses a different management solution, YMMV - but I suspect the steps will be similar. I may be able to provide information about what's configured in my work's Google Admin console, if anyone needs to compare for some reason. It depends on the exact ask.

I also gave some app "modify system settings" permissions IIRC. Don't know if that was necessary. If you were able to follow these instructions without doing so, please let me know.

I also am not an Android expert, and certainly not a Android Enterprise expert. YMMV. This seems to be working for me, but I can't comment on how close it is to official setup like you'd get on a stock Pixel device, if or how much it compromises GrapheneOS' privacy/Google sandboxing model, etc.

Your mileage may vary, for real.

Prerequisites

Before beginning, make sure you have Google Play Services installed and configured in your personal profile. You will also need at least one Google app installed - I tested with Google Drive, but my guess is that it will work with any other Google app. (If someone tests with the Google Play Store, that's probably the ideal since it's already required to be installed - so let me know if that works and I'll update the instructions.)

You will also need to have adb installed on your computer, and your computer connected to your Android device. Make sure that adb devices shows device next to your device's serial number, and that adb shell works. I'm not going to write detailed instructions for setting this stuff up because there's lots of resources out there already, but if someone links good instructions, I'd be happy to edit that link into this post.

Instructions to set up the work profile

First, initiate work profile setup by adding your Google Account in your personal profile. Watch out for the "Action required in Play Store" notification asking you to install the device config app - allow the app install, then switch back to the work profile setup screen. At the "Checking info..." screen, it will crash.

Still in your personal profile, open Google Drive and switch to your work Google Account (it will have a blank icon in the account switcher) to trigger work profile setup again. Tap "Accept and continue" to start setting up the work profile. Wait, then tap "Next" when the process is done.

Wait some more. You'll see a spinning screen for a while, then "Can't set up work profile".

In an adb shell, verify that a work profile has been partially created:

tokay:/ $ pm list users
Users:
	UserInfo{0:John Doe:4c13} running
	UserInfo{10:Work profile:1070} running

You will need to double-check that the work profile ID is 10, and adjust below commands if not. Notably, deleting a work profile and setting it up again apparently changes the ID.

The underlying issue is that Play Services is expecting a freshly-provisioned work profile to already have Play Services installed, because normally it comes with the system image. But since this isn't the case with GrapheneOS, we can simply manually intervene in the work profile to install Google Play Services. Once again in an adb shell:

tokay:/ $ pm install-existing --user 10 com.google.android.gms
Package com.google.android.gms installed for user: 10
tokay:/ $ pm install-existing --user 10 com.android.vending  
Package com.android.vending installed for user: 10

Go back to work profile setup and tap "Try again". You will see a "Checking info..." screen. Watch out for the GmsCompat notification coming from the work profile to enable notification permissions for Google Play Store/Services.

You may be asked to sign in again. If you copied your password, pasting it won't work, presumably because the thing is running in the work profile context now. Good luck to the freaks like me who decided they "might as well" generate 100+ character passwords.

After this, you can proceed through the work profile setup flow. On the screen that prompts you to install work apps, be sure to watch out for the GmsCompat notification telling you to open Google Play Store and allow installations.

Congrats! You have a (pardon the pun, working) work profile!

    Well-written guide, thanks for contributing that!

    Just a word of warning -
    Many (most?) US employers will stipulate clauses in their employment contracts that give them the right to confiscate your device indefinitely on the thinnest pretense if there is company data of ANY kind on it.

    This is nominally so that they can protect their IP by snooping on your device and see what kinds of company data you've been handling with it.

    This is the norm in the US, and I imagine it is a similar to the situation in many parts of the world.

    If you ever have an issue with your employer, the last thing you want is for your personal device to be confiscated at a critical time of legal conflict with the org that pays / paid your bills.
    I highly recommend keeping work and personal data siloed on separate devices for this reason.
    Review your employment contract's provisions related to work data on work devices and think critically about your threat modelling before deciding which phone to use this way.

    Stay safe out there everyone ~

      thank you for the guide - well done.

      just out of curiosity: What is the difference to apps like shelter?

      I did the following steps: installed shelter (from droid-ify in my case) - setup work profile with shelter app.
      no hassle (install adb, enable usb debugging, etc), no cli (and therefore no chance for typos or mistakes), guided, and with additional features
      no far no issues or complaints with the work environment..

      note: i am aware and comfortable with adb or a cli in general, but this might not be the case for everyone.

        DaRon
        This is the use case for which work profiles are designed. An employer has an MDM system setup, in this case it is Googles solution which uses a Google app to setup and configure the work profile. The employer will have the work profile configured to their security requirements, also will likely install and possibly configure apps that are designed to work with MDM systems or profile accounts.
        The employer has ownership and in most cases remote control of the work profile.

          Carlos-Anso

          i got that, thank you... but i did not ask why you (might) need a work profile. I did ask though, why so complicated? Shelter does exactly the same thing - and even more. I guess, there are a few people out there who are not familiar (or simply not interested) with working on command line.
          Thats why i was asking: why the detour with adb when you can have the same result way easier? Or did i miss something?

          Again, use it (GrapheneOS + Shelter + "Intune and whatever the employer is asking for") since P6a - without any issues.

          note: i am not connected to or affiliated by "shelter" at all - it just does a flawless job

            DaRon I believe MDM solutions will only really work with the "Google edition" of Work Profiles. In that MDM you can configure policies that might be mandatory and will most likely not work in Shelter (since it doesn't use all of the APIs normally found in Googles WP).

            ah....alright. Thanks for clarification.
            Didn't know that there are multiple ways (=different MDM solutions) to integrate a device into a companies network.

            DaRon
            The app that creates the work profile has ownership of it and configures numerous properties during setup and in the case of most MDM apps can remotely control them after setup. This includes things like enforcing the complexity of the unlock method, defining which apps can use IPC between work and personal profile,
            Also allows for things like remotely locking the work profile.

            The majority of MDM solutions are likely to provide the ability to require that the work profile is created by their app so that the company utilizing the MDM service has the expected control. The company utilizing the MDM is able set things up to suit their security requirements.

            zzz Many (most?) US employers will stipulate clauses in their employment contracts that give them the right to confiscate your device indefinitely on the thinnest pretense if there is company data of ANY kind on it.

            Is it possible to leverage this clause into them providing you with a company phone? 😂
            I think I'd prefer a work-only device that could be turned off and dropped inside a faraday bag when leaving the premises.

            DaRon I am aware of Shelter, and in fact until last week used it to manage my work profile (I've been working at my current job since September 2021). Just to give some examples of the practical effects of what @Carlos-Anso is describing:

            • Google apps never worked properly for me in the Shelter profile. They install and open, but get stuck at the "Checking info..." screen so you can never actually use them.
            • Almost certainly related to the above, my Shelter-manged profile does not show up as a managed device in Google Admin, and therefore can't be e.g. remote wiped by IT if I leave the company. (The work profile that is, not the entire device.) The "proper" work profile I set up with these instructions shows up fine.

            At my work we're likely going to start requiring that devices attest that they're enrolled in the MDM when the user logs into work applications on them, so we can ensure sensitive work data is properly wiped when folks leave the company. A Shelter work profile would fail this attestation requirement. If your work ever starts enforcing a similar attestation check with Intune, your Shelter profile would likely start failing that check too.

              strugee
              Thank you so much for the explanation. I learned that there are different of "levels" a company can enforce (or not) things...
              Conclusion: i have been lucky so far :-)
              Will try your approach to be prepared - just in case

              thanks

              Are there employers who expect this, but don't provide a cell phone? Amd what happens when people say "I don't have a smart phone"?

              This guide is well-written however.

                angela Are there employers who expect this, but don't provide a cell phone?

                Yes, often so that the IT department can save money.

                Amd what happens when people say "I don't have a smart phone"?

                Depends on the employer.
                A good one would likely ask a new employee to buy a new device then get it reimbursed, with a pre-agreed cap on how much they'll reimburse.

                angela

                angela Are there employers who expect this, but don't provide a cell phone? Amd what happens when people say "I don't have a smart phone"?

                Can't comment on other employers but $work is an early-mid stage startup. It's hard to justify the cost of buying everyone a phone, yes.

                It doesn't matter if someone doesn't have a smart phone (actually, this has already happened!) because they can't really access work data on a dumb phone anyway. The purpose isn't to track employees or anything (though I understand the suspicion and I'm sure there are places that set more invasive policies than we do). It's just to have some amount of control over company data. Especially because we handle people's PII.

                It's also not all about security. A large chunk of it, unfortunately, is for us to be able to check a box for (sometimes not technically sophisticated) auditors who don't care about discussing actual security tradeoffs. They just want to check their checkbox.

                Obviously this is a terrible and frankly irritating way to design systems. But if we refused to work with auditors that thought like this, we'd go out of business.

                a month later

                I tried this on my Pixel 6a, but I cannot get this to work. My company is also using Google MDM. After I logged in the second time, Google Play Services just crashes and if I begin again it just shows "Can't add work profile" ...

                This is the crashlog:

                type: crash
                osVersion: google/bluejay/bluejay:15/AP4A.250105.002/2025012700:user/release-keys
                flags: dev options enabled
                package: com.google.android.gms:245034035, targetSdk 35
                process: com.google.android.gms.ui
                processUptime: 5854440 + 215 ms
                installer: app.grapheneos.apps
                GmsCompatConfig version: 153
                
                java.lang.SecurityException: Permission Denial: starting Intent { act=android.app.action.PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE cmp=com.android.managedprovisioning/.PreProvisioningActivityViaTrustedApp (has extras) } from ProcessRecord{a9027e6 14023:com.google.android.gms.ui/u0a154} (pid=14023, uid=10154) requires android.permission.DISPATCH_PROVISIONING_MESSAGE
                	at android.os.Parcel.createExceptionOrNull(Parcel.java:3240)
                	at android.os.Parcel.createException(Parcel.java:3224)
                	at android.os.Parcel.readException(Parcel.java:3200)
                	at android.os.Parcel.readException(Parcel.java:3142)
                	at android.app.IActivityTaskManager$Stub$Proxy.startActivity(IActivityTaskManager.java:2051)
                	at android.app.Instrumentation.execStartActivity(Instrumentation.java:2013)
                	at android.app.Activity.startActivityForResult(Activity.java:5928)
                	at pqz.platform_startActivityForResult(:com.google.android.gms@245034035@24.50.34 (260400-713002902):2)
                	at akwk.platform_startActivityForResult(:com.google.android.gms@245034035@24.50.34 (260400-713002902):4)
                	at pqy.startActivityForResult(:com.google.android.gms@245034035@24.50.34 (260400-713002902):2)
                	at com.google.android.chimera.android.Activity.startActivityForResult(:com.google.android.gms@245034035@24.50.34 (260400-713002902):2)
                	at pvb.startActivityForResult(:com.google.android.gms@245034035@24.50.34 (260400-713002902):2)
                	at pqw.support_startActivityForResult(:com.google.android.gms@245034035@24.50.34 (260400-713002902):2)
                	at prb.startActivityForResult(:com.google.android.gms@245034035@24.50.34 (260400-713002902):2)
                	at pwc.startActivityForResult(:com.google.android.gms@245034035@24.50.34 (260400-713002902):4)
                	at prb.public_startActivityForResult(:com.google.android.gms@245034035@24.50.34 (260400-713002902):2)
                	at pqz.startActivityForResult(:com.google.android.gms@245034035@24.50.34 (260400-713002902):3)
                	at android.app.Activity.startActivityForResult(Activity.java:5847)
                	at pqz.platform_startActivityForResult(:com.google.android.gms@245034035@24.50.34 (260400-713002902):1)
                	at akwk.platform_startActivityForResult(:com.google.android.gms@245034035@24.50.34 (260400-713002902):2)
                	at pqy.startActivityForResult(:com.google.android.gms@245034035@24.50.34 (260400-713002902):1)
                	at com.google.android.chimera.android.Activity.startActivityForResult(:com.google.android.gms@245034035@24.50.34 (260400-713002902):1)
                	at pvb.startActivityForResult(:com.google.android.gms@245034035@24.50.34 (260400-713002902):1)
                	at pqw.support_startActivityForResult(:com.google.android.gms@245034035@24.50.34 (260400-713002902):1)
                	at prb.startActivityForResult(:com.google.android.gms@245034035@24.50.34 (260400-713002902):1)
                	at pwc.startActivityForResult(:com.google.android.gms@245034035@24.50.34 (260400-713002902):2)
                	at abca.x(:com.google.android.gms@245034035@24.50.34 (260400-713002902):848)
                	at abca.b(:com.google.android.gms@245034035@24.50.34 (260400-713002902):28)
                	at hau.jR(:com.google.android.gms@245034035@24.50.34 (260400-713002902):25)
                	at gwn.kc(:com.google.android.gms@245034035@24.50.34 (260400-713002902):29)
                	at gwn.f(:com.google.android.gms@245034035@24.50.34 (260400-713002902):42)
                	at gwn.l(:com.google.android.gms@245034035@24.50.34 (260400-713002902):15)
                	at hat.l(:com.google.android.gms@245034035@24.50.34 (260400-713002902):1)
                	at hat.onLoadComplete(:com.google.android.gms@245034035@24.50.34 (260400-713002902):21)
                	at hbe.deliverResult(:com.google.android.gms@245034035@24.50.34 (260400-713002902):5)
                	at aojm.deliverResult(:com.google.android.gms@245034035@24.50.34 (260400-713002902):9)
                	at hay.c(:com.google.android.gms@245034035@24.50.34 (260400-713002902):33)
                	at hbh.run(:com.google.android.gms@245034035@24.50.34 (260400-713002902):15)
                	at android.os.Handler.handleCallback(Handler.java:991)
                	at android.os.Handler.dispatchMessage(Handler.java:102)
                	at android.os.Looper.loopOnce(Looper.java:232)
                	at android.os.Looper.loop(Looper.java:317)
                	at android.app.ActivityThread.main(ActivityThread.java:8826)
                	at java.lang.reflect.Method.invoke(Native Method)
                	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:591)
                	at com.android.internal.os.ExecInit.main(ExecInit.java:50)
                	at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
                	at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:369)
                Caused by: android.os.RemoteException: Remote stack trace:
                	at com.android.server.wm.ActivityTaskSupervisor.checkStartAnyActivityPermission(ActivityTaskSupervisor.java:1177)
                	at com.android.server.wm.ActivityStarter.executeRequest(ActivityStarter.java:1150)
                	at com.android.server.wm.ActivityStarter.execute(ActivityStarter.java:790)
                	at com.android.server.wm.ActivityTaskManagerService.startActivityAsUser(ActivityTaskManagerService.java:1311)
                	at com.android.server.wm.ActivityTaskManagerService.startActivityAsUser(ActivityTaskManagerService.java:1256)```