diff --git a/app/build.gradle b/app/build.gradle index 5e8bab82..2b798207 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,7 +9,7 @@ ext { // exactly 1 digit versionMinor = 0 // exactly 2 digits - versionBuild = 05 + versionBuild = 06 } android { diff --git a/app/src/main/java/com/afwsamples/testdpc/PackageMonitorReceiver.java b/app/src/main/java/com/afwsamples/testdpc/PackageMonitorReceiver.java index 991f1eda..b175056d 100644 --- a/app/src/main/java/com/afwsamples/testdpc/PackageMonitorReceiver.java +++ b/app/src/main/java/com/afwsamples/testdpc/PackageMonitorReceiver.java @@ -8,7 +8,6 @@ import android.support.v7.app.NotificationCompat; import android.support.v7.app.NotificationCompat.Builder; import android.text.TextUtils; - import com.afwsamples.testdpc.common.NotificationUtil; public class PackageMonitorReceiver extends BroadcastReceiver { @@ -37,6 +36,7 @@ public void onReceive(Context context, Intent intent) { .setContentText(notificationBody) .setStyle(new NotificationCompat.BigTextStyle().bigText(notificationBody)) .setDefaults(Notification.DEFAULT_LIGHTS) + .setOnlyAlertOnce(true) .build(); NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); diff --git a/app/src/main/java/com/afwsamples/testdpc/common/Util.java b/app/src/main/java/com/afwsamples/testdpc/common/Util.java index 1a1f4da2..d4b74d25 100644 --- a/app/src/main/java/com/afwsamples/testdpc/common/Util.java +++ b/app/src/main/java/com/afwsamples/testdpc/common/Util.java @@ -30,6 +30,7 @@ import android.net.Uri; import android.os.Build; import android.os.Build.VERSION_CODES; +import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; import android.support.annotation.RequiresApi; @@ -56,7 +57,12 @@ */ public class Util { private static final String TAG = "Util"; - private static final int DEFAULT_BUFFER_SIZE = 4096; + private static final int DEFAULT_BUFFER_SIZE = 4096; + + private static final String BROADCAST_ACTION_FRP_CONFIG_CHANGED = + "com.google.android.gms.auth.FRP_CONFIG_CHANGED"; + private static final String GMSCORE_PACKAGE = "com.google.android.gms"; + private static final String PERSISTENT_DEVICE_OWNER_STATE = "persistentDeviceOwnerState"; /** * Format a friendly datetime for the current locale according to device policy documentation. @@ -194,6 +200,42 @@ public static boolean installCaCertificate(InputStream certificateInputStream, return false; } + /** + * Returns the persistent device owner state which has been set by the device owner as an app + * restriction on GmsCore or null if there is no such restriction set. + */ + @TargetApi(VERSION_CODES.O) + public static String getPersistentDoStateFromApplicationRestriction( + DevicePolicyManager dpm, ComponentName admin) { + Bundle restrictions = dpm.getApplicationRestrictions(admin, GMSCORE_PACKAGE); + return restrictions.getString(PERSISTENT_DEVICE_OWNER_STATE); + } + + /** + * Sets the persistent device owner state by setting a special app restriction on GmsCore and + * notifies GmsCore about the change by sending a broadcast. + * + * @param state The device owner state to be preserved across factory resets. If null, the + * persistent device owner state and the corresponding restiction are cleared. + */ + @TargetApi(VERSION_CODES.O) + public static void setPersistentDoStateWithApplicationRestriction( + Context context, DevicePolicyManager dpm, ComponentName admin, String state) { + Bundle restrictions = dpm.getApplicationRestrictions(admin, GMSCORE_PACKAGE); + if (state == null) { + // Clear the restriction + restrictions.remove(PERSISTENT_DEVICE_OWNER_STATE); + } else { + // Set the restriction + restrictions.putString(PERSISTENT_DEVICE_OWNER_STATE, state); + } + dpm.setApplicationRestrictions(admin, GMSCORE_PACKAGE, restrictions); + Intent broadcastIntent = new Intent(BROADCAST_ACTION_FRP_CONFIG_CHANGED); + broadcastIntent.setPackage(GMSCORE_PACKAGE); + broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + context.sendBroadcast(broadcastIntent); + } + private static DevicePolicyManager getDevicePolicyManager(Context context) { return (DevicePolicyManager)context.getSystemService(Service.DEVICE_POLICY_SERVICE); } diff --git a/app/src/main/java/com/afwsamples/testdpc/common/preference/DpcPreferenceHelper.java b/app/src/main/java/com/afwsamples/testdpc/common/preference/DpcPreferenceHelper.java index f41a7f81..15c38869 100644 --- a/app/src/main/java/com/afwsamples/testdpc/common/preference/DpcPreferenceHelper.java +++ b/app/src/main/java/com/afwsamples/testdpc/common/preference/DpcPreferenceHelper.java @@ -93,9 +93,6 @@ public class DpcPreferenceHelper { * not yet assigned. */ private int getDeviceSdkInt() { - if (BuildCompat.isAtLeastO()) { - return Build.VERSION_CODES.O; - } return Build.VERSION.SDK_INT; } diff --git a/app/src/main/java/com/afwsamples/testdpc/policy/PersistentDeviceOwnerFragment.java b/app/src/main/java/com/afwsamples/testdpc/policy/PersistentDeviceOwnerFragment.java new file mode 100644 index 00000000..2d256379 --- /dev/null +++ b/app/src/main/java/com/afwsamples/testdpc/policy/PersistentDeviceOwnerFragment.java @@ -0,0 +1,77 @@ +package com.afwsamples.testdpc.policy; + +import android.annotation.TargetApi; +import android.app.Fragment; +import android.app.admin.DevicePolicyManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; + +import com.afwsamples.testdpc.DeviceAdminReceiver; +import com.afwsamples.testdpc.R; +import com.afwsamples.testdpc.common.Util; + +/** + * Allows the user to set a test persistent device owner state. + * + *

For manual testing of forced re-enrollment. + * + *

If there is a non-empty peristent device owner state, it will survive the next factory reset, + * TestDPC will be re-installed automatically as device owner and the state will be passed to it + * during the initial device setup. + */ +public class PersistentDeviceOwnerFragment extends Fragment implements View.OnClickListener { + + private DevicePolicyManager mDpm; + private ComponentName mAdminComponent; + private EditText mStateEdit; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getActivity().getActionBar().setTitle(R.string.persistent_device_owner); + mDpm = (DevicePolicyManager) getActivity().getSystemService( + Context.DEVICE_POLICY_SERVICE); + mAdminComponent = DeviceAdminReceiver.getComponentName(getActivity()); + } + + @Override + public View onCreateView( + LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View root = inflater.inflate(R.layout.persistent_device_owner_fragment, container, false); + root.findViewById(R.id.clear_persistent_device_owner_button).setOnClickListener(this); + root.findViewById(R.id.set_persistent_device_owner_button).setOnClickListener(this); + mStateEdit = (EditText) root.findViewById(R.id.persistent_device_owner_state_edit); + return root; + } + + @Override + public void onClick(View view) { + String message = null; + switch (view.getId()) { + case R.id.clear_persistent_device_owner_button: + mStateEdit.getText().clear(); + Util.setPersistentDoStateWithApplicationRestriction( + getActivity(), mDpm, mAdminComponent, null); + break; + case R.id.set_persistent_device_owner_button: + Util.setPersistentDoStateWithApplicationRestriction( + getActivity(), mDpm, mAdminComponent, mStateEdit.getText().toString()); + break; + } + } + + @Override + public void onResume() { + super.onResume(); + String state = Util.getPersistentDoStateFromApplicationRestriction(mDpm, mAdminComponent); + mStateEdit.setText(state == null ? "" : state); + } +} diff --git a/app/src/main/java/com/afwsamples/testdpc/policy/PolicyManagementFragment.java b/app/src/main/java/com/afwsamples/testdpc/policy/PolicyManagementFragment.java index 06f88d13..b97b0e99 100644 --- a/app/src/main/java/com/afwsamples/testdpc/policy/PolicyManagementFragment.java +++ b/app/src/main/java/com/afwsamples/testdpc/policy/PolicyManagementFragment.java @@ -298,6 +298,7 @@ public class PolicyManagementFragment extends BaseSearchablePolicyPreferenceFrag private static final String UNHIDE_APPS_KEY = "unhide_apps"; private static final String UNSUSPEND_APPS_KEY = "unsuspend_apps"; private static final String WIPE_DATA_KEY = "wipe_data"; + private static final String PERSISTENT_DEVICE_OWNER_KEY = "persistent_device_owner"; private static final String CREATE_WIFI_CONFIGURATION_KEY = "create_wifi_configuration"; private static final String CREATE_EAP_TLS_WIFI_CONFIGURATION_KEY = "create_eap_tls_wifi_configuration"; @@ -409,6 +410,7 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { STAY_ON_WHILE_PLUGGED_IN); mStayOnWhilePluggedInSwitchPreference.setOnPreferenceChangeListener(this); findPreference(WIPE_DATA_KEY).setOnPreferenceClickListener(this); + findPreference(PERSISTENT_DEVICE_OWNER_KEY).setOnPreferenceClickListener(this); findPreference(REMOVE_DEVICE_OWNER_KEY).setOnPreferenceClickListener(this); mEnableBackupServicePreference = (SwitchPreference) findPreference(ENABLE_BACKUP_SERVICE); mEnableBackupServicePreference.setOnPreferenceChangeListener(this); @@ -482,6 +484,10 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { SET_AUTO_TIME_REQUIRED_KEY); mSetAutoTimeRequiredPreference.setOnPreferenceChangeListener(this); + mSetDeviceOrganizationNamePreference = + (EditTextPreference) findPreference(SET_DEVICE_ORGANIZATION_NAME_KEY); + mSetDeviceOrganizationNamePreference.setOnPreferenceChangeListener(this); + constrainSpecialCasePreferences(); maybeDisableLockTaskPreferences(); @@ -585,6 +591,9 @@ public void onPositiveButtonClicked(String[] lockTaskArray) { case WIPE_DATA_KEY: showWipeDataPrompt(); return true; + case PERSISTENT_DEVICE_OWNER_KEY: + showFragment(new PersistentDeviceOwnerFragment()); + return true; case REMOVE_DEVICE_OWNER_KEY: showRemoveDeviceOwnerPrompt(); return true; diff --git a/app/src/main/java/com/afwsamples/testdpc/provision/PostProvisioningTask.java b/app/src/main/java/com/afwsamples/testdpc/provision/PostProvisioningTask.java index 67cb0946..be4de37b 100644 --- a/app/src/main/java/com/afwsamples/testdpc/provision/PostProvisioningTask.java +++ b/app/src/main/java/com/afwsamples/testdpc/provision/PostProvisioningTask.java @@ -37,6 +37,7 @@ import android.util.Log; import com.afwsamples.testdpc.AddAccountActivity; +import com.afwsamples.testdpc.DeviceAdminReceiver; import com.afwsamples.testdpc.EnableDeviceOwnerActivity; import com.afwsamples.testdpc.EnableProfileActivity; import com.afwsamples.testdpc.common.LaunchIntentUtil; @@ -66,6 +67,8 @@ public class PostProvisioningTask { "com.afwsamples.testdpc.SetupManagementLaunchActivity"; private static final String POST_PROV_PREFS = "post_prov_prefs"; private static final String KEY_POST_PROV_DONE = "key_post_prov_done"; + private static final String KEY_DEVICE_OWNER_STATE = + "android.app.extra.PERSISTENT_DEVICE_OWNER_STATE"; private final Context mContext; private final DevicePolicyManager mDevicePolicyManager; @@ -98,6 +101,18 @@ public boolean performPostProvisioningOperations(Intent intent) { maybeSetAffiliationIds(extras); } + // If TestDPC asked GmsCore to store its state in the FRP area before factory reset, the + // state will be handed over to it during the next device setup. + if (BuildCompat.isAtLeastOMR1() + && extras != null + && extras.containsKey(KEY_DEVICE_OWNER_STATE)) { + Util.setPersistentDoStateWithApplicationRestriction( + mContext, + mDevicePolicyManager, + DeviceAdminReceiver.getComponentName(mContext), + extras.getString(KEY_DEVICE_OWNER_STATE)); + } + // Hide the setup launcher when this app is the admin mContext.getPackageManager().setComponentEnabledSetting( new ComponentName(mContext, SETUP_MANAGEMENT_LAUNCH_ACTIVITY), diff --git a/app/src/main/res/layout/persistent_device_owner_fragment.xml b/app/src/main/res/layout/persistent_device_owner_fragment.xml new file mode 100644 index 00000000..92543834 --- /dev/null +++ b/app/src/main/res/layout/persistent_device_owner_fragment.xml @@ -0,0 +1,46 @@ + + + + + +