am 97e554ab: Merge "BYOD provisioning flow in CtsVerifier" into lmp-dev

* commit '97e554ab21eceb5283de9e5b5071186b21c72061':
  BYOD provisioning flow in CtsVerifier
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 0183fa2..9f83d92 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1239,6 +1239,40 @@
                  android:label="@string/projection_service_name"
                  android:process=":projectionservice" />
 
-   </application>
+
+        <activity android:name=".managedprovisioning.ByodFlowTestActivity"
+                android:launchMode="singleTask"
+                android:label="@string/provisioning_byod">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_STATUS" />
+                <category android:name="android.intent.category.DEFAULT"></category>
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_managed_provisioning" />
+        </activity>
+
+        <activity android:name=".managedprovisioning.ByodHelperActivity">
+            <intent-filter>
+                <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_QUERY" />
+                <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_REMOVE" />
+                <category android:name="android.intent.category.DEFAULT"></category>
+            </intent-filter>
+        </activity>
+
+        <receiver android:name=".managedprovisioning.DeviceAdminTestReceiver"
+                android:label="@string/provisioning_byod_device_admin"
+                android:permission="android.permission.BIND_DEVICE_ADMIN">
+            <meta-data android:name="android.app.device_admin"
+                       android:resource="@xml/device_admin_byod" />
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+                <action android:name="android.app.action.PROFILE_PROVISIONING_COMPLETE"/>
+            </intent-filter>
+        </receiver>
+
+    </application>
 
 </manifest>
diff --git a/apps/CtsVerifier/res/layout/provisioning_byod.xml b/apps/CtsVerifier/res/layout/provisioning_byod.xml
new file mode 100644
index 0000000..5e59558
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/provisioning_byod.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        >
+
+    <ScrollView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1">
+        <TextView
+                android:id="@+id/byod_instructions"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:padding="10dip"
+                android:text="@string/provisioning_byod_instructions"
+                android:textSize="18dip" />
+    </ScrollView>
+
+    <Button
+        android:id="@+id/byod_start"
+        android:layout_width="204dp"
+        android:layout_height="wrap_content"
+        android:text="@string/provisioning_byod_start" />
+
+    <ListView
+        android:id="@id/android:list"
+        android:layout_width="match_parent"
+        android:layout_height="258dp" />
+
+    <include layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 38e0ced..2cae77b 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1188,4 +1188,60 @@
     <string name="snsr_rotation_vector_set_final">Place the device back to the reference position.</string>
     <string name="snsr_rotation_vector_verification">Angular deviation [%1$4.1f %2$4.1f %3$4.1f]. Current: %4$f deg. Max tolerated: %5$f.</string>
 
+    <!-- Strings for BYOD managed provisioning tests (ByodFlowTestActivity) -->
+    <string name="test_category_managed_provisioning">Managed Provisioning</string>
+    <string name="provisioning_byod_device_admin">CTS Verifier - BYOD Admin</string>
+    <string name="provisioning_byod">BYOD Managed Provisioning</string>
+    <string name="provisioning_byod_info">
+        This test exercises the BYOD managed provisioning flow.
+        Start by pressing the button on screen and follow instructions to finish the managed provisioning process.
+        If your device has not been encrypted before, it will be encrypted and rebooted.
+        After the provisioning process completes, return to this page and carry out further verifications.
+    </string>
+    <string name="provisioning_byod_start">Start BYOD provisioning flow</string>
+    <string name="provisioning_byod_instructions">
+        1. Press the button below to start the managed provisioning flow.
+        If your device has not been encrypted before, it will reboot to apply encryption.
+        After reboot follow instructions in the notification area to complete the provisioning.\n
+        2. After successful provisioning, you should be automatically redirected back to this page.
+        Please press through the following verification steps.
+        Allow a few seconds after returning from provisioning, as the profile owner test should automatically pass.
+    </string>
+    <string name="provisioning_byod_profileowner">Profile owner installed</string>
+    <string name="provisioning_byod_diskencryption">Full disk encryption enabled</string>
+    <string name="provisioning_byod_profile_visible">Work profile visible in Settings</string>
+    <string name="provisioning_byod_admin_visible">Device administrator visible in Settings</string>
+    <string name="provisioning_byod_workapps_visible">Badged work apps visible in Launcher</string>
+    <string name="provisioning_byod_profile_visible_instruction">
+        Please press the Go button to open the Settings page.
+        Navigate to Accounts and confirm that:\n
+        \n
+        - Both Personal and Work categories exist.\n
+        - \"Remove work profile\" exists under the Work category.\n
+        \n
+        Use the Back button to return to this page.
+    </string>
+    <string name="provisioning_byod_admin_visible_instruction">
+        Please press the Go button to open the Security page in Settings.
+        Navigate to Device administrators and confirm that:\n
+        \n
+        - Both Personal and Work categories exist.\n
+        - \"CTS Verifier - BYOD Admin\" exists under the Work category, and is activated.\n
+        \n
+        Use the Back button to return to this page.
+    </string>
+    <string name="provisioning_byod_workapps_visible_instruction">
+        Please press the Go button to start the launcher.
+        Go to All Apps screen and scroll through it to confirm that:\n
+        \n
+        - A new set of work apps including CTS Verifier appear in the list.\n
+        - Work badge overlay appears on work app\'s icon.\n
+        \n
+        Then navigate back to this screen using Recents button.
+    </string>
+    <string name="provisioning_byod_no_activity">Cannot communicate with activity in the work profile.</string>
+    <string name="provisioning_byod_delete_profile">Initiate deletion of work profile.</string>
+    <string name="provisioning_byod_profile_deleted">Work profile deleted.</string>
+    <string name="provisioning_byod_disabled">Device provisioning is not enabled.</string>
+    <string name="provisioning_byod_go">Go</string>
 </resources>
diff --git a/apps/CtsVerifier/res/xml/device_admin_byod.xml b/apps/CtsVerifier/res/xml/device_admin_byod.xml
new file mode 100644
index 0000000..0408ce2
--- /dev/null
+++ b/apps/CtsVerifier/res/xml/device_admin_byod.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<!-- BEGIN_INCLUDE(meta_data) -->
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
+    <uses-policies>
+        <encrypted-storage />
+        <wipe-data />
+    </uses-policies>
+</device-admin>
+<!-- END_INCLUDE(meta_data) -->
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
new file mode 100644
index 0000000..69071f6
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2012 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 com.android.cts.verifier.managedprovisioning;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.admin.DevicePolicyManager;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestListActivity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * CTS verifier test for BYOD managed provisioning flow.
+ * This activity is responsible for starting the managed provisioning flow and verify the outcome of provisioning.
+ * It performs the following verifications:
+ *   Full disk encryption is enabled.
+ *   Profile owner is correctly installed.
+ *   Profile owner shows up in the Settings app.
+ *   Badged work apps show up in launcher.
+ * The first two verifications are performed automatically, by interacting with profile owner using
+ * cross-profile intents, while the last two are carried out manually by the user.
+ */
+public class ByodFlowTestActivity extends PassFailButtons.ListActivity {
+
+    private final String TAG = "ByodFlowTestActivity";
+    private static final int REQUEST_STATUS = 1;
+
+    private ComponentName mAdminReceiverComponent;
+
+    private TestAdapter mAdapter;
+    private View mStartProvisioningButton;
+    private List<TestItem> mTests = new ArrayList<TestItem>();
+
+    protected DevicePolicyManager mDevicePolicyManager;
+
+    private TestItem mProfileOwnerInstalled;
+    private TestItem mDiskEncryptionTest;
+    private TestItem mProfileVisibleTest;
+    private TestItem mDeviceAdminVisibleTest;
+    private TestItem mWorkAppVisibleTest;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mAdminReceiverComponent = new ComponentName(this, DeviceAdminTestReceiver.class.getName());
+        mDevicePolicyManager = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
+        disableComponent();
+
+        setContentView(R.layout.provisioning_byod);
+        setInfoResources(R.string.provisioning_byod, R.string.provisioning_byod_info, -1);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+        setResult(RESULT_CANCELED);
+
+        setupTests();
+
+        mAdapter = new TestAdapter(this);
+        setListAdapter(mAdapter);
+        mAdapter.addAll(mTests);
+
+        mStartProvisioningButton = findViewById(R.id.byod_start);
+        mStartProvisioningButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                startByodProvisioning();
+            }
+        });
+
+        // If we are started by managed provisioning (fresh managed provisioning after encryption
+        // reboot), redirect the user back to the main test list. This is because the test result
+        // is only saved by the parent TestListActivity, and if we did allow the user to proceed
+        // here, the test result would be lost when this activity finishes.
+        if (ByodHelperActivity.ACTION_PROFILE_OWNER_STATUS.equals(getIntent().getAction())) {
+            startActivity(new Intent(this, TestListActivity.class));
+            // Calling super.finish() because we delete managed profile in our overridden of finish(),
+            // which is not what we want to do here.
+            super.finish();
+        } else {
+            queryProfileOwner(false);
+        }
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        // This is called when managed provisioning completes successfully without reboot.
+        super.onNewIntent(intent);
+        if (ByodHelperActivity.ACTION_PROFILE_OWNER_STATUS.equals(intent.getAction())) {
+            handleStatusUpdate(intent);
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        // Called after queryProfileOwner()
+        super.onActivityResult(requestCode, resultCode, data);
+        if (requestCode == REQUEST_STATUS && resultCode == RESULT_OK) {
+            handleStatusUpdate(data);
+        }
+    }
+
+    private void handleStatusUpdate(Intent data) {
+        boolean provisioned = data.getBooleanExtra(ByodHelperActivity.EXTRA_PROVISIONED, false);
+        setTestResult(mProfileOwnerInstalled, provisioned ? TestResult.Passed : TestResult.Failed);
+    }
+
+    @Override
+    public void finish() {
+        // Pass and fail buttons are known to call finish() when clicked, and this is when we want to
+        // clean up the provisioned profile.
+        requestDeleteProfileOwner();
+        super.finish();
+    }
+
+    private void setupTests() {
+        mProfileOwnerInstalled = new TestItem(this, R.string.provisioning_byod_profileowner) {
+            @Override
+            public void performTest(ByodFlowTestActivity activity) {
+                queryProfileOwner(true);
+            }
+        };
+
+        mDiskEncryptionTest = new TestItem(this, R.string.provisioning_byod_diskencryption) {
+            @Override
+            public TestResult getPassFailState() {
+                return isDeviceEncrypted() ? TestResult.Passed : TestResult.Failed;
+            }
+        };
+
+        mProfileVisibleTest = new TestItem(this, R.string.provisioning_byod_profile_visible,
+                R.string.provisioning_byod_profile_visible_instruction,
+                new Intent(Settings.ACTION_SETTINGS));
+
+        mDeviceAdminVisibleTest = new TestItem(this, R.string.provisioning_byod_admin_visible,
+                R.string.provisioning_byod_admin_visible_instruction,
+                new Intent(Settings.ACTION_SECURITY_SETTINGS));
+
+        mWorkAppVisibleTest = new TestItem(this, R.string.provisioning_byod_workapps_visible,
+                R.string.provisioning_byod_workapps_visible_instruction,
+                new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME));
+
+        mTests.add(mDiskEncryptionTest);
+        mTests.add(mProfileOwnerInstalled);
+        mTests.add(mProfileVisibleTest);
+        mTests.add(mDeviceAdminVisibleTest);
+        mTests.add(mWorkAppVisibleTest);
+    }
+
+    @Override
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        super.onListItemClick(l, v, position, id);
+        TestItem test = (TestItem) getListAdapter().getItem(position);
+        test.performTest(this);
+    }
+
+    private void showManualTestDialog(final TestItem test) {
+        AlertDialog dialog = new AlertDialog.Builder(this)
+                .setIcon(android.R.drawable.ic_dialog_info)
+                .setMessage(test.getManualTestInstruction())
+                .setNeutralButton(R.string.provisioning_byod_go, null)
+                .setPositiveButton(R.string.pass_button_text, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        setTestResult(test, TestResult.Passed);
+                    }
+                })
+                .setNegativeButton(R.string.fail_button_text, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        setTestResult(test, TestResult.Failed);
+                    }
+                })
+                .create();
+        dialog.show();
+
+        dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                ByodFlowTestActivity.this.startActivity(test.getManualTestIntent());
+            }
+        });
+    }
+
+    private void setTestResult(TestItem test, TestResult result) {
+        test.setPassFailState(result);
+
+        boolean testSucceeds = true;
+        for(TestItem aTest : mTests) {
+            testSucceeds &= (aTest.getPassFailState() == TestResult.Passed);
+        }
+        getPassButton().setEnabled(testSucceeds);
+        mAdapter.notifyDataSetChanged();
+    }
+
+    private void startByodProvisioning() {
+        Intent sending = new Intent(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE);
+        sending.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME,
+                mAdminReceiverComponent.getPackageName());
+        sending.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mAdminReceiverComponent);
+
+        if (sending.resolveActivity(getPackageManager()) != null) {
+            // ManagedProvisioning must be started with startActivityForResult, but we don't
+            // care about the result, so passing 0 as a requestCode
+            startActivityForResult(sending, 0);
+        } else {
+            showToast(R.string.provisioning_byod_disabled);
+        }
+    }
+
+    private void queryProfileOwner(boolean showToast) {
+        try {
+            Intent intent = new Intent(ByodHelperActivity.ACTION_QUERY_PROFILE_OWNER);
+            startActivityForResult(intent, REQUEST_STATUS);
+        }
+        catch (ActivityNotFoundException e) {
+            Log.d(TAG, "queryProfileOwner: ActivityNotFoundException", e);
+            setTestResult(mProfileOwnerInstalled, TestResult.Failed);
+            if (showToast) {
+                showToast(R.string.provisioning_byod_no_activity);
+            }
+        }
+    }
+
+    private void requestDeleteProfileOwner() {
+        try {
+            Intent intent = new Intent(ByodHelperActivity.ACTION_REMOVE_PROFILE_OWNER);
+            startActivity(intent);
+            showToast(R.string.provisioning_byod_delete_profile);
+        }
+        catch (ActivityNotFoundException e) {
+            Log.d(TAG, "requestDeleteProfileOwner: ActivityNotFoundException", e);
+        }
+    }
+
+    private void disableComponent() {
+        // Disable app components in the current profile, so only the counterpart in the other profile
+        // can respond (via cross-profile intent filter)
+        getPackageManager().setComponentEnabledSetting(new ComponentName(
+                this, ByodHelperActivity.class),
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                PackageManager.DONT_KILL_APP);
+    }
+
+    private boolean isDeviceEncrypted() {
+        return mDevicePolicyManager.getStorageEncryptionStatus()
+                == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE;
+    }
+
+    private void showToast(int messageId) {
+        String message = getString(messageId);
+        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
+    }
+
+    enum TestResult {
+        Unknown, Failed, Passed
+    }
+
+    static class TestItem {
+
+        private String mDisplayName;
+        private TestResult mPassed;
+        private boolean mManualTest;
+        private String mManualInstruction;
+        private Intent mManualIntent;
+
+        public TestItem(Context context, int nameResId) {
+            mDisplayName = context.getString(nameResId);
+            mPassed = TestResult.Unknown;
+            mManualTest = false;
+        }
+
+        public void performTest(ByodFlowTestActivity activity) {
+            if (isManualTest()) {
+                activity.showManualTestDialog(this);
+            }
+        }
+
+        public TestItem(Context context, int nameResId, int testInstructionResId, Intent testIntent) {
+            mDisplayName = context.getString(nameResId);
+            mPassed = TestResult.Unknown;
+            mManualTest = true;
+            mManualInstruction = context.getString(testInstructionResId);
+            mManualIntent = testIntent;
+        }
+
+        @Override
+        public String toString() {
+            return mDisplayName;
+        }
+
+        TestResult getPassFailState() {
+            return mPassed;
+        }
+
+        void setPassFailState(TestResult state) {
+            mPassed = state;
+        }
+
+        public boolean isManualTest() {
+            return mManualTest;
+        }
+
+        public String getManualTestInstruction() {
+            return mManualInstruction;
+        }
+
+        public Intent getManualTestIntent() {
+            return mManualIntent;
+        }
+    }
+
+    static class TestAdapter extends ArrayAdapter<TestItem> {
+
+        public TestAdapter(Context context) {
+            super(context, android.R.layout.simple_list_item_1);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            TextView view = (TextView) super.getView(position, convertView, parent);
+
+            TestItem item = getItem(position);
+            int backgroundResource = 0;
+            int iconResource = 0;
+            if (item.getPassFailState() == TestResult.Passed) {
+                backgroundResource = R.drawable.test_pass_gradient;
+                iconResource = R.drawable.fs_good;
+            } else if (item.getPassFailState() == TestResult.Failed){
+                backgroundResource = R.drawable.test_fail_gradient;
+                iconResource = R.drawable.fs_error;
+            }
+            view.setBackgroundResource(backgroundResource);
+            view.setPadding(10, 0, 10, 0);
+            view.setCompoundDrawablePadding(10);
+            view.setCompoundDrawablesWithIntrinsicBounds(0, 0, iconResource, 0);
+
+            return view;
+        }
+    }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
new file mode 100644
index 0000000..5dac4bd
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2012 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 com.android.cts.verifier.managedprovisioning;
+
+import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.managedprovisioning.ByodFlowTestActivity.TestResult;
+
+/**
+ * A helper activity from the managed profile side that responds to requests from CTS verifier in
+ * primary user. Profile owner APIs are accessible inside this activity (given this activity is
+ * started within the work profile). Its current functionalities include making sure the profile
+ * owner is setup correctly, and removing the work profile upon request.
+ *
+ * Note: We have to use a dummy activity because cross-profile intents only work for activities.
+ */
+public class ByodHelperActivity extends Activity {
+    static final String TAG = "ByodHelperActivity";
+
+    // Primary -> managed intent: query if the profile owner has been set up.
+    public static final String ACTION_QUERY_PROFILE_OWNER = "com.android.cts.verifier.managedprovisioning.BYOD_QUERY";
+    // Managed -> primary intent: update profile owner test status in primary's CtsVerifer
+    public static final String ACTION_PROFILE_OWNER_STATUS = "com.android.cts.verifier.managedprovisioning.BYOD_STATUS";
+    // Primary -> managed intent: request to delete the current profile
+    public static final String ACTION_REMOVE_PROFILE_OWNER = "com.android.cts.verifier.managedprovisioning.BYOD_REMOVE";
+    // Managed -> managed intent: provisioning completed successfully
+    public static final String ACTION_PROFILE_PROVISIONED = "com.android.cts.verifier.managedprovisioning.BYOD_PROVISIONED";
+
+    public static final String EXTRA_PROVISIONED = "extra_provisioned";
+
+    private ComponentName mAdminReceiverComponent;
+
+    private DevicePolicyManager mDevicePolicyManager;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mAdminReceiverComponent = new ComponentName(this, DeviceAdminTestReceiver.class.getName());
+        mDevicePolicyManager = (DevicePolicyManager) getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
+        String action = getIntent().getAction();
+        Log.d(TAG, "ByodHelperActivity.onCreate: " + action);
+
+        // we are explicitly started by {@link DeviceAdminTestReceiver} after a successful provisioning.
+        if (action.equals(ACTION_PROFILE_PROVISIONED)) {
+            // Jump back to CTS verifier with result.
+            Intent response = new Intent(ACTION_PROFILE_OWNER_STATUS);
+            response.putExtra(EXTRA_PROVISIONED, isProfileOwner());
+            startActivityInPrimary(response);
+            // Queried by CtsVerifier in the primary side using startActivityForResult.
+        } else if (action.equals(ACTION_QUERY_PROFILE_OWNER)) {
+            Intent response = new Intent();
+            response.putExtra(EXTRA_PROVISIONED, isProfileOwner());
+            setResult(RESULT_OK, response);
+            // Request to delete work profile.
+        } else if (action.equals(ACTION_REMOVE_PROFILE_OWNER)) {
+            if (isProfileOwner()) {
+                mDevicePolicyManager.wipeData(0);
+                showToast(R.string.provisioning_byod_profile_deleted);
+            }
+        }
+        // This activity has no UI and is only used to respond to CtsVerifier in the primary side.
+        finish();
+    }
+
+    private boolean isProfileOwner() {
+        return mDevicePolicyManager.isAdminActive(mAdminReceiverComponent) &&
+                mDevicePolicyManager.isProfileOwnerApp(mAdminReceiverComponent.getPackageName());
+    }
+
+    private void startActivityInPrimary(Intent intent) {
+        // Disable app components in the current profile, so only the counterpart in the other
+        // profile can respond (via cross-profile intent filter)
+        getPackageManager().setComponentEnabledSetting(new ComponentName(
+                this, ByodFlowTestActivity.class),
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                PackageManager.DONT_KILL_APP);
+        startActivity(intent);
+    }
+
+    private void showToast(int messageId) {
+        String message = getString(messageId);
+        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
new file mode 100644
index 0000000..861bca2
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2012 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 com.android.cts.verifier.managedprovisioning;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.util.Log;
+import android.widget.Toast;
+
+/**
+ * Profile owner receiver for BYOD flow test.
+ * Setup cross-profile intent filter after successful provisioning.
+ */
+public class DeviceAdminTestReceiver extends DeviceAdminReceiver {
+        private static final String TAG = "DeviceAdminTestReceiver";
+
+        @Override
+        public void onProfileProvisioningComplete(Context context, Intent intent) {
+            Log.d(TAG, "Provisioning complete intent received");
+            setupProfile(context);
+        }
+
+        private void setupProfile(Context context) {
+            DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
+            dpm.setProfileEnabled(new ComponentName(context.getApplicationContext(), getClass()));
+
+            // Setup cross-profile intent filter to allow communications between the two versions of CtsVerifier
+            // Primary -> work direction
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(ByodHelperActivity.ACTION_QUERY_PROFILE_OWNER);
+            filter.addAction(ByodHelperActivity.ACTION_REMOVE_PROFILE_OWNER);
+            dpm.addCrossProfileIntentFilter(getWho(context), filter,
+                    DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
+
+            // Work -> primary direction
+            filter = new IntentFilter();
+            filter.addAction(ByodHelperActivity.ACTION_PROFILE_OWNER_STATUS);
+            dpm.addCrossProfileIntentFilter(getWho(context), filter,
+                    DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
+
+            Intent intent = new Intent(context, ByodHelperActivity.class);
+            intent.setAction(ByodHelperActivity.ACTION_PROFILE_PROVISIONED);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            context.startActivity(intent);
+        }
+}