diff --git a/app/build.gradle b/app/build.gradle index 5459fea8..d321e546 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,7 +13,7 @@ ext { } android { - compileSdkVersion 'android-P' + compileSdkVersion 28 buildToolsVersion "28.0.0" defaultConfig { @@ -32,16 +32,8 @@ android { } } - flavorDimensions "targetSdk", "packageName" + flavorDimensions "packageName" productFlavors { - targetP { - dimension 'targetSdk' - targetSdkVersion 'P' - } - targetO { - dimension 'targetSdk' - targetSdkVersion 27 - } normal { dimension 'packageName' } @@ -87,11 +79,10 @@ android { dependencies { implementation 'com.android.support:multidex:1.0.1' - implementation 'com.android.support:preference-v14:27.0.2' - implementation 'com.android.support:recyclerview-v7:27.0.2' - implementation 'com.android.support:support-v13:27.0.2' + implementation 'com.android.support:preference-v14:28.0.0-SNAPSHOT' + implementation 'com.android.support:recyclerview-v7:28.0.0-SNAPSHOT' + implementation 'com.android.support:support-v13:28.0.0-SNAPSHOT' implementation 'com.android.support.constraint:constraint-layout:1.0.2' - implementation 'com.google.android.gms:play-services-safetynet:+' implementation(name: 'setup-wizard-lib-platform-release', ext: 'aar') implementation 'org.bouncycastle:bcpkix-jdk15on:1.56' implementation 'org.bouncycastle:bcprov-jdk15on:1.56' diff --git a/app/libs/setup-wizard-lib-platform-release.aar b/app/libs/setup-wizard-lib-platform-release.aar index 9a50d09d..2e1c3d0d 100644 Binary files a/app/libs/setup-wizard-lib-platform-release.aar and b/app/libs/setup-wizard-lib-platform-release.aar differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8e7e2c47..1543b7e7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -26,7 +26,7 @@ - + @@ -63,22 +64,17 @@ + android:theme="@style/SuwThemeGlifV3.Light"/> - - + android:theme="@style/SuwThemeGlifV3.Light"/> + android:theme="@style/SuwThemeGlifV3.Light"/> - @@ -121,17 +114,6 @@ - - - - - - - - - diff --git a/app/src/main/java/com/afwsamples/testdpc/AddAccountActivity.java b/app/src/main/java/com/afwsamples/testdpc/AddAccountActivity.java index 7904259f..06169e91 100644 --- a/app/src/main/java/com/afwsamples/testdpc/AddAccountActivity.java +++ b/app/src/main/java/com/afwsamples/testdpc/AddAccountActivity.java @@ -35,8 +35,7 @@ import android.widget.RadioGroup; import android.widget.Toast; -import com.android.setupwizardlib.SetupWizardLayout; -import com.android.setupwizardlib.view.NavigationBar; +import com.android.setupwizardlib.GlifLayout; import java.io.IOException; @@ -45,7 +44,7 @@ * It is responsible for adding an account to the managed profile (Profile Owner) or managed device * (Device Owner). */ -public class AddAccountActivity extends Activity implements NavigationBar.NavigationBarListener { +public class AddAccountActivity extends Activity { private static final String TAG = "AddAccountActivity"; private static final String GOOGLE_ACCOUNT_TYPE = "com.google"; @@ -70,10 +69,8 @@ protected void onCreate(Bundle savedInstanceState) { mAdminComponentName = DeviceAdminReceiver.getComponentName(this); setContentView(R.layout.activity_add_account); - SetupWizardLayout layout = (SetupWizardLayout) findViewById(R.id.setup_wizard_layout); - layout.getNavigationBar().setNavigationBarListener(this); - NavigationBar navigationBar = layout.getNavigationBar(); - navigationBar.getBackButton().setEnabled(false); + GlifLayout layout = findViewById(R.id.setup_wizard_layout); + layout.findViewById(R.id.next_button).setOnClickListener(this::onNavigateNext); Bundle extras = getIntent().getExtras(); if (extras != null) { @@ -145,9 +142,8 @@ private void restoreUserRestrictions() { } } - @Override - public void onNavigateNext() { - RadioGroup addAccountOptions = (RadioGroup) findViewById(R.id.add_account_options); + public void onNavigateNext(View nextButton) { + RadioGroup addAccountOptions = findViewById(R.id.add_account_options); switch (addAccountOptions.getCheckedRadioButtonId()) { case R.id.add_account: addAccount(null); @@ -168,14 +164,10 @@ public void onNavigateNext() { break; case R.id.add_account_skip: if (mNextActivityIntent != null) { - mNextActivityIntent.putExtra(EnableProfileActivity.EXTRA_ENABLE_PROFILE_NOW, true); startActivity(mNextActivityIntent); } finish(); break; } } - - @Override - public void onNavigateBack() {} } diff --git a/app/src/main/java/com/afwsamples/testdpc/EnableDeviceOwnerActivity.java b/app/src/main/java/com/afwsamples/testdpc/EnableDeviceOwnerActivity.java deleted file mode 100644 index 0acfc3fd..00000000 --- a/app/src/main/java/com/afwsamples/testdpc/EnableDeviceOwnerActivity.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2015 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.afwsamples.testdpc; - -import android.app.Activity; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.util.Log; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import com.afwsamples.testdpc.common.LaunchIntentUtil; -import com.android.setupwizardlib.SetupWizardLayout; -import com.android.setupwizardlib.view.NavigationBar; - -/** - * This activity is started after device owner provisioning is complete in - * {@link DeviceAdminReceiver}. - */ -public class EnableDeviceOwnerActivity extends Activity - implements NavigationBar.NavigationBarListener { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.enable_device_owner_activity); - SetupWizardLayout layout = (SetupWizardLayout) findViewById(R.id.setup_wizard_layout); - NavigationBar navigationBar = layout.getNavigationBar(); - navigationBar.setNavigationBarListener(this); - navigationBar.getNextButton().setText(R.string.finish_button); - - ImageView appIcon = (ImageView) findViewById(R.id.app_icon); - TextView appLabel = (TextView) findViewById(R.id.app_label); - try { - PackageManager packageManager = getPackageManager(); - ApplicationInfo applicationInfo = packageManager.getApplicationInfo( - getPackageName(), 0 /* Default flags */); - appIcon.setImageDrawable(packageManager.getApplicationIcon(applicationInfo)); - appLabel.setText(packageManager.getApplicationLabel(applicationInfo)); - } catch (PackageManager.NameNotFoundException e) { - Log.w("TestDPC", "Couldn't look up our own package?!?!", e); - } - - // Show the user which account now has management, if specified. - String addedAccount = getIntent().getStringExtra(LaunchIntentUtil.EXTRA_ACCOUNT_NAME); - if (addedAccount != null) { - findViewById(R.id.managed_account_name_label).setVisibility(View.VISIBLE); - - TextView managedAccountName = (TextView) findViewById(R.id.managed_account_name); - managedAccountName.setText(addedAccount); - managedAccountName.setVisibility(View.VISIBLE); - } - } - - @Override - public void onNavigateBack() { - onBackPressed(); - } - - @Override - public void onNavigateNext() { - finish(); - } -} diff --git a/app/src/main/java/com/afwsamples/testdpc/EnableProfileActivity.java b/app/src/main/java/com/afwsamples/testdpc/EnableProfileActivity.java deleted file mode 100644 index 8079e7eb..00000000 --- a/app/src/main/java/com/afwsamples/testdpc/EnableProfileActivity.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2015 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.afwsamples.testdpc; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.app.Activity; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.support.annotation.StringRes; -import android.support.v4.content.LocalBroadcastManager; -import android.util.Log; -import android.view.View; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.TextView; - -import com.afwsamples.testdpc.common.LaunchIntentUtil; -import com.afwsamples.testdpc.provision.CheckInState; -import com.afwsamples.testdpc.provision.ProvisioningUtil; -import com.android.setupwizardlib.SetupWizardLayout; -import com.android.setupwizardlib.view.NavigationBar; - -import static com.afwsamples.testdpc.provision.CheckInState.FIRST_ACCOUNT_READY_PROCESSED_ACTION; - -/** - * This activity is started after managed profile provisioning is complete in - * {@link DeviceAdminReceiver}. There could be two cases: - * 1. If we are not going to add account now, we will then enable profile immediately. - * 2. If we have just added account, we need to wait for the FIRST_ACCOUNT_READY broadcast before - * enabling the profile. The broadcast indicates that the account has been synced with Google - * and is ready for use. - */ -public class EnableProfileActivity extends Activity implements NavigationBar.NavigationBarListener { - private CheckInStateReceiver mCheckInStateReceiver; - private Button mFinishButton; - private SetupWizardLayout mSetupWizardLayout; - - private CheckInState mCheckinState; - - public static final String EXTRA_ENABLE_PROFILE_NOW = "enable_profile_now"; - private static final IntentFilter sIntentFilter = - new IntentFilter(FIRST_ACCOUNT_READY_PROCESSED_ACTION); - private static final long WAIT_FOR_FIRST_ACCOUNT_READY_TIMEOUT = 60 * 1000; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mCheckinState = new CheckInState(this); - if (savedInstanceState == null) { - if (getIntent().getBooleanExtra(EXTRA_ENABLE_PROFILE_NOW, false)) { - mCheckinState.setFirstAccountState(CheckInState.FIRST_ACCOUNT_STATE_READY); - ProvisioningUtil.enableProfile(this); - } else { - // Set up an alarm to enable profile in case we do not receive first account ready - // broadcast for whatever reason. - FirstAccountReadyBroadcastReceiver.scheduleFirstAccountReadyTimeoutAlarm( - this, WAIT_FOR_FIRST_ACCOUNT_READY_TIMEOUT); - } - } - setContentView(R.layout.enable_profile_activity); - mSetupWizardLayout = (SetupWizardLayout) findViewById(R.id.setup_wizard_layout); - NavigationBar navigationBar = mSetupWizardLayout.getNavigationBar(); - navigationBar.getBackButton().setEnabled(false); - navigationBar.setNavigationBarListener(this); - mFinishButton = navigationBar.getNextButton(); - mFinishButton.setText(R.string.finish_button); - - mCheckInStateReceiver = new CheckInStateReceiver(); - - // This is just a user friendly shortcut to the policy management screen of this app. - ImageView appIcon = (ImageView) findViewById(R.id.app_icon); - TextView appLabel = (TextView) findViewById(R.id.app_label); - try { - PackageManager packageManager = getPackageManager(); - ApplicationInfo applicationInfo = packageManager.getApplicationInfo( - getPackageName(), 0 /* Default flags */); - appIcon.setImageDrawable(packageManager.getApplicationIcon(applicationInfo)); - appLabel.setText(packageManager.getApplicationLabel(applicationInfo)); - } catch (PackageManager.NameNotFoundException e) { - Log.w("TestDPC", "Couldn't look up our own package?!?!", e); - } - - // Show the user which account now has management, if specified. - String addedAccount = getIntent().getStringExtra(LaunchIntentUtil.EXTRA_ACCOUNT_NAME); - if (addedAccount != null) { - View accountMigrationStatusLayout; - if (isAccountMigrated(addedAccount)) { - accountMigrationStatusLayout = findViewById(R.id.account_migration_success); - } else { - accountMigrationStatusLayout = findViewById(R.id.account_migration_fail); - } - accountMigrationStatusLayout.setVisibility(View.VISIBLE); - TextView managedAccountName = (TextView) accountMigrationStatusLayout.findViewById( - R.id.managed_account_name); - managedAccountName.setText(addedAccount); - } - } - - @Override - protected void onResume() { - super.onResume(); - LocalBroadcastManager.getInstance(this).registerReceiver(mCheckInStateReceiver, - sIntentFilter); - // In case the broadcast is sent before we register the receiver. - refreshUi(); - } - - @Override - protected void onStop() { - super.onStop(); - LocalBroadcastManager.getInstance(this).unregisterReceiver(mCheckInStateReceiver); - } - - private boolean isAccountMigrated(String addedAccount) { - Account[] accounts = AccountManager.get(this).getAccounts(); - for (Account account : accounts) { - if (addedAccount.equalsIgnoreCase(account.name)) { - return true; - } - } - return false; - } - - private void refreshUi() { - boolean enableFinish; - @StringRes int headerTextResId; - switch (mCheckinState.getFirstAccountState()) { - case CheckInState.FIRST_ACCOUNT_STATE_READY: - enableFinish = true; - headerTextResId = R.string.finish_setup; - break; - case CheckInState.FIRST_ACCOUNT_STATE_TIMEOUT: - enableFinish = true; - headerTextResId = R.string.finish_setup_account_not_ready; - break; - case CheckInState.FIRST_ACCOUNT_STATE_PENDING: - default: - enableFinish = false; - headerTextResId = R.string.waiting_for_first_account_check_in; - break; - } - if (enableFinish) { - mSetupWizardLayout.hideProgressBar(); - } else { - mSetupWizardLayout.showProgressBar(); - } - mSetupWizardLayout.setHeaderText(headerTextResId); - mFinishButton.setEnabled(enableFinish); - } - - @Override - public void onNavigateBack() { - onBackPressed(); - } - - @Override - public void onNavigateNext() { - finish(); - } - - class CheckInStateReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - // Processed the first check-in broadcast, allow user to tap the finish button. - refreshUi(); - } - } -} diff --git a/app/src/main/java/com/afwsamples/testdpc/FinalizeActivity.java b/app/src/main/java/com/afwsamples/testdpc/FinalizeActivity.java new file mode 100644 index 00000000..61140c0c --- /dev/null +++ b/app/src/main/java/com/afwsamples/testdpc/FinalizeActivity.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2015 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.afwsamples.testdpc; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.app.Activity; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; + +import com.afwsamples.testdpc.common.LaunchIntentUtil; +import com.afwsamples.testdpc.common.Util; +import com.afwsamples.testdpc.provision.ProvisioningUtil; +import com.android.setupwizardlib.GlifLayout; + +public class FinalizeActivity extends Activity { + private Button mFinishButton; + private GlifLayout mSetupWizardLayout; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (savedInstanceState == null) { + if (Util.isManagedProfileOwner(this)) { + ProvisioningUtil.enableProfile(this); + } + } + setContentView(R.layout.finalize_activity); + mSetupWizardLayout = findViewById(R.id.setup_wizard_layout); + mFinishButton = mSetupWizardLayout.findViewById(R.id.next_button); + mFinishButton.setText(R.string.finish_button); + mFinishButton.setOnClickListener(this::onNavigateNext); + + + // This is just a user friendly shortcut to the policy management screen of this app. + ImageView appIcon = findViewById(R.id.app_icon); + TextView appLabel = findViewById(R.id.app_label); + try { + PackageManager packageManager = getPackageManager(); + ApplicationInfo applicationInfo = packageManager.getApplicationInfo( + getPackageName(), 0 /* Default flags */); + appIcon.setImageDrawable(packageManager.getApplicationIcon(applicationInfo)); + appLabel.setText(packageManager.getApplicationLabel(applicationInfo)); + } catch (PackageManager.NameNotFoundException e) { + Log.w("TestDPC", "Couldn't look up our own package?!?!", e); + } + + // Show the user which account now has management, if specified. + String addedAccount = getIntent().getStringExtra(LaunchIntentUtil.EXTRA_ACCOUNT_NAME); + if (addedAccount != null) { + View accountMigrationStatusLayout; + if (isAccountMigrated(addedAccount)) { + accountMigrationStatusLayout = findViewById(R.id.account_migration_success); + } else { + accountMigrationStatusLayout = findViewById(R.id.account_migration_fail); + } + accountMigrationStatusLayout.setVisibility(View.VISIBLE); + TextView managedAccountName = (TextView) accountMigrationStatusLayout.findViewById( + R.id.managed_account_name); + managedAccountName.setText(addedAccount); + } + + ((TextView) findViewById(R.id.explanation)).setText(Util.isDeviceOwner(this) + ? R.string.all_done_explanation_device_owner + : R.string.all_done_explanation_profile_owner); + } + + private boolean isAccountMigrated(String addedAccount) { + Account[] accounts = AccountManager.get(this).getAccounts(); + for (Account account : accounts) { + if (addedAccount.equalsIgnoreCase(account.name)) { + return true; + } + } + return false; + } + + public void onNavigateNext(View nextButton) { + finish(); + } +} diff --git a/app/src/main/java/com/afwsamples/testdpc/FirstAccountReadyBroadcastReceiver.java b/app/src/main/java/com/afwsamples/testdpc/FirstAccountReadyBroadcastReceiver.java deleted file mode 100644 index 61efe57c..00000000 --- a/app/src/main/java/com/afwsamples/testdpc/FirstAccountReadyBroadcastReceiver.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2016 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.afwsamples.testdpc; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.os.SystemClock; -import android.util.Log; - -import com.afwsamples.testdpc.common.Util; -import com.afwsamples.testdpc.provision.CheckInState; -import com.afwsamples.testdpc.provision.ProvisioningUtil; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -/** - * Receiver for FIRST_ACCOUNT_READY_ACTION from Google Play Service. - * Receiver only matters for Managed Profile flow, so we ignore the broadcast in other cases. - */ -public class FirstAccountReadyBroadcastReceiver extends BroadcastReceiver { - private static final String TAG = "FirstAccountReady"; - - private static final String FIRST_ACCOUNT_READY_ACTION = - "com.google.android.work.action.FIRST_ACCOUNT_READY"; - - public static final String FIRST_ACCOUNT_READY_TIMEOUT_ACTION = - "com.afwsamples.testdpc.FIRST_ACCOUNT_READY_TIMEOUT"; - - private static final Set SUPPORTED_ACTIONS = new HashSet<>( - Arrays.asList(FIRST_ACCOUNT_READY_ACTION, FIRST_ACCOUNT_READY_TIMEOUT_ACTION)); - - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - Log.d(TAG, "Received: " + action); - - if (!Util.isManagedProfileOwner(context)) { - Log.d(TAG, "Not a Managed Profile case. Ignoring broadcast."); - return; - } - - if (!SUPPORTED_ACTIONS.contains(action)) { - Log.d(TAG, String.format("Action %s not supported by receiver %s. Ignoring broadcast.", - action, getClass().getName())); - return; - } - - CheckInState checkInState = new CheckInState(context); - if (checkInState.getFirstAccountState() == CheckInState.FIRST_ACCOUNT_STATE_PENDING) { - checkInState.setFirstAccountState(FIRST_ACCOUNT_READY_ACTION.equals(action) - ? CheckInState.FIRST_ACCOUNT_STATE_READY - : CheckInState.FIRST_ACCOUNT_STATE_TIMEOUT); - - ProvisioningUtil.enableProfile(context); - } - } - - /** - * Enable profile anyway if we cannot receive the broadcast after certain amount time. - */ - public static void scheduleFirstAccountReadyTimeoutAlarm(Context context, long timeout) { - AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + timeout, - createFirstAccountReadyTimeoutPendingIntent(context)); - } - - public static void cancelFirstAccountReadyTimeoutAlarm(Context context) { - AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - alarmManager.cancel(createFirstAccountReadyTimeoutPendingIntent(context)); - } - - private static PendingIntent createFirstAccountReadyTimeoutPendingIntent(Context context) { - Intent intent = new Intent(context, FirstAccountReadyBroadcastReceiver.class); - intent.setAction(FirstAccountReadyBroadcastReceiver.FIRST_ACCOUNT_READY_TIMEOUT_ACTION); - return PendingIntent.getBroadcast( - context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/afwsamples/testdpc/SetupManagementFragment.java b/app/src/main/java/com/afwsamples/testdpc/SetupManagementFragment.java index 26c75481..462281a5 100644 --- a/app/src/main/java/com/afwsamples/testdpc/SetupManagementFragment.java +++ b/app/src/main/java/com/afwsamples/testdpc/SetupManagementFragment.java @@ -16,15 +16,6 @@ package com.afwsamples.testdpc; -import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE; -import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE; -import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE; -import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE; -import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME; -import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME; -import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_LOGO_URI; -import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_MAIN_COLOR; - import android.accounts.Account; import android.annotation.TargetApi; import android.app.Activity; @@ -52,23 +43,34 @@ import android.widget.RadioGroup; import android.widget.TextView; import android.widget.Toast; + import com.afwsamples.testdpc.common.ColorPicker; import com.afwsamples.testdpc.common.LaunchIntentUtil; import com.afwsamples.testdpc.common.ProvisioningStateUtil; import com.afwsamples.testdpc.common.Util; -import com.android.setupwizardlib.SetupWizardLayout; +import com.android.setupwizardlib.GlifLayout; import com.android.setupwizardlib.view.NavigationBar; + import java.security.SecureRandom; import java.util.Collections; import java.util.Set; +import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE; +import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE; +import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE; +import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE; +import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME; +import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME; +import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_LOGO_URI; +import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_MAIN_COLOR; + /** * This {@link Fragment} shows the UI that allows the user to start the setup of a managed profile * or configuration of a device-owner if the device is in an appropriate state. */ public class SetupManagementFragment extends Fragment implements - NavigationBar.NavigationBarListener, View.OnClickListener, - ColorPicker.OnColorSelectListener, RadioGroup.OnCheckedChangeListener { + View.OnClickListener, ColorPicker.OnColorSelectListener, + RadioGroup.OnCheckedChangeListener { // Tag for creating this fragment. This tag can be used to retrieve this fragment. public static final String FRAGMENT_TAG = "SetupManagementFragment"; @@ -105,17 +107,12 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, mCurrentColor = savedInstanceState.getInt(EXTRA_PROVISIONING_MAIN_COLOR); } - // Use setupwizard theme - final Context contextThemeWrapper = - new ContextThemeWrapper(getActivity(), R.style.SetupTheme); - LayoutInflater themeInflater = inflater.cloneInContext(contextThemeWrapper); - View view = themeInflater.inflate(R.layout.setup_management_fragment, container, false); - SetupWizardLayout layout = (SetupWizardLayout) view.findViewById(R.id.setup_wizard_layout); - NavigationBar navigationBar = layout.getNavigationBar(); - navigationBar.setNavigationBarListener(this); - navigationBar.getBackButton().setText(R.string.exit); - mNavigationNextButton = navigationBar.getNextButton(); - mNavigationNextButton.setText(R.string.setup_label); + View view = inflater.inflate(R.layout.setup_management_fragment, container, false); + GlifLayout layout = view.findViewById(R.id.setup_wizard_layout); + + mNavigationNextButton = layout.findViewById(R.id.setup_button); + mNavigationNextButton.setOnClickListener(this::onNavigateNext); + layout.findViewById(R.id.exit_button).setOnClickListener(this::onNavigateBack); mSetupManagementMessage = (TextView) view.findViewById(R.id.setup_management_message_id); mSetupOptions = (RadioGroup) view.findViewById(R.id.setup_options); @@ -166,7 +163,6 @@ public void onSaveInstanceState(Bundle outState) { public void onResume() { super.onResume(); - getActivity().getActionBar().hide(); if (setProvisioningMethodsVisibility()) { // The extra logo uri and color are supported only from N if (BuildCompat.isAtLeastN()) { @@ -468,13 +464,11 @@ public void onColorSelected(int colorValue, String id) { updateColorUi(); } - @Override - public void onNavigateBack() { + public void onNavigateBack(View view) { getActivity().onBackPressed(); } - @Override - public void onNavigateNext() { + public void onNavigateNext(View view) { if (mSetupOptions.getCheckedRadioButtonId() == R.id.setup_managed_profile) { maybeLaunchProvisioning(ACTION_PROVISION_MANAGED_PROFILE, REQUEST_PROVISION_MANAGED_PROFILE); diff --git a/app/src/main/java/com/afwsamples/testdpc/common/NotificationUtil.java b/app/src/main/java/com/afwsamples/testdpc/common/NotificationUtil.java index 01a4934d..3bb6880e 100644 --- a/app/src/main/java/com/afwsamples/testdpc/common/NotificationUtil.java +++ b/app/src/main/java/com/afwsamples/testdpc/common/NotificationUtil.java @@ -56,6 +56,7 @@ private static void createDefaultNotificationChannel(Context context) { String appName = context.getString(R.string.app_name); NotificationChannel channel = new NotificationChannel(DEFAULT_CHANNEL_ID, appName, NotificationManager.IMPORTANCE_DEFAULT); + channel.setImportance(NotificationManager.IMPORTANCE_LOW); notificationManager.createNotificationChannel(channel); } 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 cdb4660e..fe1c37dc 100644 --- a/app/src/main/java/com/afwsamples/testdpc/common/Util.java +++ b/app/src/main/java/com/afwsamples/testdpc/common/Util.java @@ -165,17 +165,12 @@ public static List getBindDeviceAdminTargetUsers(Context context) { public static void showFileViewerForImportingCertificate(PreferenceFragment fragment, int requestCode) { - showFilePicker(fragment, "*/*", requestCode); - } - - public static void showFilePicker(PreferenceFragment fragment, String type, int requestCode) { - Intent intent = new Intent(Intent.ACTION_GET_CONTENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setTypeAndNormalize(type); + Intent certIntent = new Intent(Intent.ACTION_GET_CONTENT); + certIntent.setTypeAndNormalize("*/*"); try { - fragment.startActivityForResult(intent, requestCode); + fragment.startActivityForResult(certIntent, requestCode); } catch (ActivityNotFoundException e) { - Log.e(TAG, "showFilePicker: ", e); + Log.e(TAG, "showFileViewerForImportingCertificate: ", e); } } diff --git a/app/src/main/java/com/afwsamples/testdpc/policy/MeteredDataRestrictionInfoAdapter.java b/app/src/main/java/com/afwsamples/testdpc/policy/MeteredDataRestrictionInfoAdapter.java index 31239e0b..ff01464a 100644 --- a/app/src/main/java/com/afwsamples/testdpc/policy/MeteredDataRestrictionInfoAdapter.java +++ b/app/src/main/java/com/afwsamples/testdpc/policy/MeteredDataRestrictionInfoAdapter.java @@ -98,7 +98,7 @@ public void onClick(DialogInterface dialog, int which) { @TargetApi(28) private void setMeteredDataRestrictedPkgs(List pkgNames) { - final List excludedPkgs = mDevicePolicyManager.setMeteredDataDisabled( + final List excludedPkgs = mDevicePolicyManager.setMeteredDataDisabledPackages( DeviceAdminReceiver.getComponentName(mContext), pkgNames); if (!excludedPkgs.isEmpty()) { diff --git a/app/src/main/java/com/afwsamples/testdpc/policy/OverrideApnFragment.java b/app/src/main/java/com/afwsamples/testdpc/policy/OverrideApnFragment.java index c8906735..e326669e 100644 --- a/app/src/main/java/com/afwsamples/testdpc/policy/OverrideApnFragment.java +++ b/app/src/main/java/com/afwsamples/testdpc/policy/OverrideApnFragment.java @@ -23,6 +23,7 @@ import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; +import android.net.Uri; import android.os.Bundle; import android.support.v14.preference.SwitchPreference; import android.support.v7.preference.Preference; @@ -30,16 +31,19 @@ import android.text.TextUtils; import android.util.Log; import android.view.View; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.ArrayAdapter; import android.widget.EditText; +import android.widget.Spinner; import android.widget.Toast; + import com.afwsamples.testdpc.DeviceAdminReceiver; import com.afwsamples.testdpc.R; import com.afwsamples.testdpc.common.BaseSearchablePolicyPreferenceFragment; + import java.net.InetAddress; -import java.net.MalformedURLException; -import java.net.URL; import java.net.UnknownHostException; -import java.util.Arrays; import java.util.List; @TargetApi(28) @@ -110,6 +114,27 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { return false; } + void setUpSpinner(View dialogView, int viewId, int textArrayId) { + final Spinner spinner = (Spinner) dialogView.findViewById(viewId); + final ArrayAdapter adapter = ArrayAdapter.createFromResource( + getActivity(), textArrayId, android.R.layout.simple_spinner_item); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinner.setAdapter(adapter); + } + + void setUpAllSpinners(View dialogView) { + // Set up spinner for auth type. + setUpSpinner(dialogView, R.id.apn_auth_type, R.array.apn_auth_type_choices); + // Set up spinner for protocol. + setUpSpinner(dialogView, R.id.apn_protocol, R.array.apn_protocol_choices); + // Set up spinner for roaming protocol. + setUpSpinner(dialogView, R.id.apn_roaming_protocol, R.array.apn_protocol_choices); + // Set up spinner for mvno type. + setUpSpinner(dialogView, R.id.apn_mvno_type, R.array.apn_mvno_type_choices); + // Set up spinner for carrier enabled. + setUpSpinner(dialogView, R.id.apn_carrier_enabled, R.array.apn_carrier_enabled_choices); + } + void showInsertOverrideApnDialog() { if (getActivity() == null || getActivity().isFinishing()) { return; @@ -135,26 +160,19 @@ void showInsertOverrideApnDialog() { R.id.apn_user); final EditText passwordEditText = (EditText) dialogView.findViewById( R.id.apn_password); - final EditText authTypeEditText = (EditText) dialogView.findViewById( - R.id.apn_auth_type); final EditText typeEditText = (EditText) dialogView.findViewById( R.id.apn_type); final EditText numericEditText = (EditText) dialogView.findViewById( R.id.apn_numeric); - final EditText protocolEditText = (EditText) dialogView.findViewById( - R.id.apn_protocol); - final EditText roamingProtocolEditText = (EditText) dialogView.findViewById( - R.id.apn_roaming_protocol); - final EditText carrierEnabledEditText = (EditText) dialogView.findViewById( - R.id.apn_carrier_enabled); final EditText networkBitmaskEditText = (EditText) dialogView.findViewById( R.id.apn_network_bitmask); - final EditText mvnoTypeEditText = (EditText) dialogView.findViewById( - R.id.apn_mvno_type); + setUpAllSpinners(dialogView); + + entryNameEditText.setHint(R.string.apn_entry_name_cannot_be_empty); + apnNameEditText.setHint(R.string.apn_name_cannot_be_empty); + typeEditText.setHint(R.string.apn_type_cannot_be_zero); - authTypeEditText.setHint(R.string.apn_auth_type_hint); numericEditText.setHint(R.string.apn_numeric_hint); - carrierEnabledEditText.setHint(R.string.apn_carrier_enabled_hint); new AlertDialog.Builder(getActivity()) .setTitle(R.string.insert_override_apn) @@ -162,17 +180,19 @@ void showInsertOverrideApnDialog() { .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> { final String entryName = entryNameEditText.getText().toString(); if (entryName.isEmpty()) { - showToast(R.string.apn_no_entry_name); + showToast(R.string.apn_entry_name_cannot_be_empty); return; } final String apnName = apnNameEditText.getText().toString(); if (apnName.isEmpty()) { - showToast(R.string.apn_no_apn_name); + showToast(R.string.apn_name_cannot_be_empty); + return; + } + final int apnTypeBitmask = parseInt(typeEditText.getText().toString(), 0); + if (apnTypeBitmask == 0) { + showToast(R.string.apn_type_cannot_be_zero); return; } - int authType = parseInt(authTypeEditText.getText().toString(), 0); - int enabled = parseInt(carrierEnabledEditText.getText().toString(), 0); - int networkbitmask = parseInt(networkBitmaskEditText.getText().toString(), 0); ApnSetting apn = makeApnSetting( numericEditText.getText().toString(), @@ -180,18 +200,28 @@ void showInsertOverrideApnDialog() { apnName, inetAddressFromString(proxyEditText.getText().toString()), parseInt(portEditText.getText().toString(), -1), - URLFromString(mmscEditText.getText().toString()), + UriFromString(mmscEditText.getText().toString()), inetAddressFromString(mmsProxyEditText.getText().toString()), parseInt(mmsPortEditText.getText().toString(), -1), userEditText.getText().toString(), passwordEditText.getText().toString(), - authType, - Arrays.asList(parseTypes(typeEditText.getText().toString())), - protocolEditText.getText().toString(), - roamingProtocolEditText.getText().toString(), - enabled == 1, - networkbitmask, - mvnoTypeEditText.getText().toString() + // -1 here as we have extra default choice "Not specified" in the + // spinner of auth type, protocol, roaming protocol and mvno type + // in case user doesn't want to specify these fields. And + // "Not Specified" should be transformed into "-1" in the builder + // of ApnSetting. + ((Spinner)dialogView.findViewById(R.id.apn_auth_type)) + .getSelectedItemPosition() - 1, + apnTypeBitmask, + ((Spinner)dialogView.findViewById(R.id.apn_protocol)) + .getSelectedItemPosition() - 1, + ((Spinner)dialogView.findViewById( + R.id.apn_roaming_protocol)).getSelectedItemPosition() - 1, + ((Spinner)dialogView.findViewById( + R.id.apn_carrier_enabled)).getSelectedItemPosition() == 1, + parseInt(networkBitmaskEditText.getText().toString(), 0), + ((Spinner)dialogView.findViewById(R.id.apn_mvno_type)) + .getSelectedItemPosition() - 1 ); int insertedId = mDevicePolicyManager.addOverrideApn(mAdminComponentName, apn); if (insertedId == -1) { @@ -221,23 +251,23 @@ private int parseInt(String str, int defaultValue) { } private ApnSetting makeApnSetting(String operatorNumeric, String entryName, String apnName, - InetAddress proxy, int port, URL mmsc, InetAddress mmsProxy, int mmsPort, - String user, String password, int authType, List types, String protocol, - String roamingProtocol, boolean carrierEnabled, int networkTypeBitmask, - String mvnoType) { - ApnSetting.Builder builder = new ApnSetting.Builder(); - return builder.setOperatorNumeric(operatorNumeric) + InetAddress proxyAddress, int proxyPort, Uri mmsc, InetAddress mmsProxyAddress, + int mmsProxyPort, String user, String password, int authType, int apnTypeBitmask, + int protocol, int roamingProtocol, boolean carrierEnabled, int networkTypeBitmask, + int mvnoType) { + return new ApnSetting.Builder() + .setOperatorNumeric(operatorNumeric) .setEntryName(entryName) .setApnName(apnName) - .setProxy(proxy) - .setPort(port) + .setProxyAddress(proxyAddress) + .setProxyPort(proxyPort) .setMmsc(mmsc) - .setMmsProxy(mmsProxy) - .setMmsPort(mmsPort) + .setMmsProxyAddress(mmsProxyAddress) + .setMmsProxyPort(mmsProxyPort) .setUser(user) .setPassword(password) .setAuthType(authType) - .setTypes(types) + .setApnTypeBitmask(apnTypeBitmask) .setProtocol(protocol) .setRoamingProtocol(roamingProtocol) .setCarrierEnabled(carrierEnabled) @@ -246,14 +276,8 @@ private ApnSetting makeApnSetting(String operatorNumeric, String entryName, Stri .build(); } - private URL URLFromString(String url) { - try { - return TextUtils.isEmpty(url) ? null : new URL(url); - } catch (MalformedURLException e) { - Log.e(LOG_TAG, "Can't parse URL from string."); - showToast(R.string.apn_wrong_url); - return null; - } + private Uri UriFromString(String uri) { + return TextUtils.isEmpty(uri) ? null : Uri.parse(uri); } private InetAddress inetAddressFromString(String inetAddress) { @@ -269,18 +293,6 @@ private InetAddress inetAddressFromString(String inetAddress) { } } - private String[] parseTypes(String types) { - String[] result; - // If unset, set to DEFAULT. - if (TextUtils.isEmpty(types)) { - result = new String[1]; - result[0] = "*"; - } else { - result = types.split(","); - } - return result; - } - private void reloadEnableOverrideApnUi() { boolean enabled = mDevicePolicyManager.isOverrideApnEnabled(mAdminComponentName); if (mEnableOverrideApnPreference.isEnabled()) { 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 08efa0d5..dea10075 100644 --- a/app/src/main/java/com/afwsamples/testdpc/policy/PolicyManagementFragment.java +++ b/app/src/main/java/com/afwsamples/testdpc/policy/PolicyManagementFragment.java @@ -52,7 +52,6 @@ import android.security.KeyChain; import android.security.KeyChainAliasCallback; import android.service.notification.NotificationListenerService; -import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; import android.support.annotation.StringRes; import android.support.v14.preference.SwitchPreference; @@ -119,7 +118,6 @@ import com.afwsamples.testdpc.profilepolicy.apprestrictions.ManageAppRestrictionsFragment; import com.afwsamples.testdpc.profilepolicy.delegation.DelegationFragment; import com.afwsamples.testdpc.profilepolicy.permission.ManageAppPermissionsFragment; -import com.afwsamples.testdpc.safetynet.SafetyNetFragment; import com.afwsamples.testdpc.transferownership.PickTransferComponentFragment; import com.afwsamples.testdpc.util.MainThreadExecutor; @@ -284,13 +282,6 @@ public class PolicyManagementFragment extends BaseSearchablePolicyPreferenceFrag private static final String MANAGE_APP_RESTRICTIONS_KEY = "manage_app_restrictions"; private static final String MANAGED_PROFILE_SPECIFIC_POLICIES_KEY = "managed_profile_policies"; private static final String MANAGE_LOCK_TASK_LIST_KEY = "manage_lock_task"; - private static final String MANDATORY_BACKUPS = "mandatory_backups"; - private static final String MANDATORY_BACKUP_ACCOUNT_CHANGED_ACTION = - "com.google.android.gms.backup.MANDATORY_BACKUP_ACCOUNT_CHANGED"; - private static final String MANDATORY_BACKUP_ACCOUNT_NAME_APP_RESTRICTION = - "mandatoryBackupAccountName"; - private static final String MANDATORY_BACKUP_ACCOUNT_TYPE_APP_RESTRICTION = - "mandatoryBackupAccountType"; private static final String MUTE_AUDIO_KEY = "mute_audio"; private static final String NETWORK_STATS_KEY = "network_stats"; private static final String PASSWORD_CONSTRAINTS_KEY = "password_constraints"; @@ -356,7 +347,6 @@ public class PolicyManagementFragment extends BaseSearchablePolicyPreferenceFrag private static final String TAG_WIFI_CONFIG_CREATION = "wifi_config_creation"; private static final String WIFI_CONFIG_LOCKDOWN_ON = "1"; private static final String WIFI_CONFIG_LOCKDOWN_OFF = "0"; - private static final String SAFETYNET_ATTEST = "safetynet_attest"; private static final String SECURITY_PATCH_FORMAT = "yyyy-MM-dd"; private static final String SET_NEW_PASSWORD = "set_new_password"; private static final String SET_PROFILE_PARENT_NEW_PASSWORD = "set_profile_parent_new_password"; @@ -377,9 +367,6 @@ public class PolicyManagementFragment extends BaseSearchablePolicyPreferenceFrag BatteryManager.BATTERY_PLUGGED_USB | BatteryManager.BATTERY_PLUGGED_WIRELESS); private static final String DONT_STAY_ON = "0"; - public static final String GMSCORE_PACKAGE = "com.google.android.gms"; - public static final String GMSCORE_BACKUP_TRANSPORT = - "com.google.android.gms.backup.BackupTransportService"; private static final int USER_OPERATION_ERROR_UNKNOWN = 1; private static final int USER_OPERATION_SUCCESS = 0; @@ -407,7 +394,6 @@ public class PolicyManagementFragment extends BaseSearchablePolicyPreferenceFrag private DpcSwitchPreference mInstallNonMarketAppsPreference; private SwitchPreference mEnableBackupServicePreference; - private SwitchPreference mMandatoryBackupsPreference; private SwitchPreference mEnableSecurityLoggingPreference; private SwitchPreference mEnableNetworkLoggingPreference; private SwitchPreference mSetAutoTimeRequiredPreference; @@ -525,8 +511,6 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { findPreference(REMOVE_DEVICE_OWNER_KEY).setOnPreferenceClickListener(this); mEnableBackupServicePreference = (SwitchPreference) findPreference(ENABLE_BACKUP_SERVICE); mEnableBackupServicePreference.setOnPreferenceChangeListener(this); - mMandatoryBackupsPreference = (SwitchPreference) findPreference(MANDATORY_BACKUPS); - mMandatoryBackupsPreference.setOnPreferenceChangeListener(this); findPreference(REQUEST_BUGREPORT_KEY).setOnPreferenceClickListener(this); mEnableSecurityLoggingPreference = (SwitchPreference) findPreference(ENABLE_SECURITY_LOGGING); @@ -599,7 +583,6 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { findPreference(REBOOT_KEY).setOnPreferenceClickListener(this); findPreference(SET_SHORT_SUPPORT_MESSAGE_KEY).setOnPreferenceClickListener(this); findPreference(SET_LONG_SUPPORT_MESSAGE_KEY).setOnPreferenceClickListener(this); - findPreference(SAFETYNET_ATTEST).setOnPreferenceClickListener(this); findPreference(SET_NEW_PASSWORD).setOnPreferenceClickListener(this); findPreference(SET_PROFILE_PARENT_NEW_PASSWORD).setOnPreferenceClickListener(this); findPreference(CROSS_PROFILE_APPS).setOnPreferenceClickListener(this); @@ -640,7 +623,6 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { reloadScreenCaptureDisableUi(); reloadMuteAudioUi(); reloadEnableBackupServiceUi(); - reloadMandatoryBackupsUi(); reloadEnableSecurityLoggingUi(); reloadEnableNetworkLoggingUi(); reloadSetAutoTimeRequiredUi(); @@ -996,10 +978,6 @@ public void onPositiveButtonClicked(String[] lockTaskArray) { showFragment(SetSupportMessageFragment.newInstance( SetSupportMessageFragment.TYPE_LONG)); return true; - case SAFETYNET_ATTEST: - DialogFragment safetynetFragment = new SafetyNetFragment(); - safetynetFragment.show(getFragmentManager(), SafetyNetFragment.class.getName()); - return true; case SET_NEW_PASSWORD: startActivity(new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD)); return true; @@ -1144,16 +1122,7 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { case ENABLE_BACKUP_SERVICE: setBackupServiceEnabled((Boolean) newValue); reloadEnableBackupServiceUi(); - reloadMandatoryBackupsUi(); return true; - case MANDATORY_BACKUPS: - if ((Boolean) newValue) { - showMandatoryBackupAccountPicker(); - return false; - } else { - setupMandatoryBackups(null); - return true; - } case ENABLE_SECURITY_LOGGING: setSecurityLoggingEnabled((Boolean) newValue); reloadEnableSecurityLoggingUi(); @@ -1281,8 +1250,8 @@ private boolean installKeyPair(final PrivateKey key, final Certificate cert, fin boolean isUserSelectable) { if (BuildCompat.isAtLeastP()) { return mDevicePolicyManager.installKeyPair( - mAdminComponentName, key, new Certificate[]{cert}, alias, false, - isUserSelectable); + mAdminComponentName, key, new Certificate[]{cert}, alias, + isUserSelectable ? DevicePolicyManager.INSTALLKEY_SET_USER_SELECTABLE : 0); } else { if (!isUserSelectable) { throw new IllegalArgumentException( @@ -2097,14 +2066,6 @@ private void reloadEnableBackupServiceUi() { } } - @TargetApi(28) - private void reloadMandatoryBackupsUi() { - if (mMandatoryBackupsPreference.isEnabled()) { - mMandatoryBackupsPreference.setChecked( - null != mDevicePolicyManager.getMandatoryBackupTransport()); - } - } - @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void reloadScreenCaptureDisableUi() { boolean isScreenCaptureDisabled = mDevicePolicyManager.getScreenCaptureDisabled( @@ -2790,7 +2751,7 @@ private void showSetMeteredDataPrompt() { @TargetApi(28) private List getMeteredDataRestrictedPkgs() { - return mDevicePolicyManager.getMeteredDataDisabled(mAdminComponentName); + return mDevicePolicyManager.getMeteredDataDisabledPackages(mAdminComponentName); } /** @@ -3118,7 +3079,7 @@ private void relaunchInLockTaskMode() { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); final ActivityOptions options = ActivityOptions.makeBasic(); - options.setLockTaskMode(true); + options.setLockTaskEnabled(true); try { startActivity(intent, options.toBundle()); @@ -3389,65 +3350,6 @@ private int validateAffiliatedUserAfterP() { } } - @TargetApi(28) - private void showMandatoryBackupAccountPicker() { - if (!BuildCompat.isAtLeastP()) { - return; - } - if (getActivity() == null || getActivity().isFinishing()) { - return; - } - List accounts = Arrays.asList(mAccountManager.getAccounts()); - if (accounts.isEmpty()) { - showToast(R.string.no_accounts_available); - } else { - AccountArrayAdapter accountArrayAdapter = - new AccountArrayAdapter(getActivity(), R.id.account_name, accounts); - new AlertDialog.Builder(getActivity()) - .setTitle(R.string.mandatory_backup_account) - .setAdapter( - accountArrayAdapter, - (dialog, position) -> setupMandatoryBackups(accounts.get(position))) - .show(); - } - } - - @TargetApi(28) - private void setupMandatoryBackups(@Nullable Account account) { - boolean makeBackupsMandatory = account != null; - // For the data to be backed up to Google Drive, set the backup transport to the GmsCore's - // backup transport. - mDevicePolicyManager.setMandatoryBackupTransport(mAdminComponentName, makeBackupsMandatory ? - new ComponentName(GMSCORE_PACKAGE, GMSCORE_BACKUP_TRANSPORT) : null); - - // Set app restrictions with the account type and name on GmsCore to let it know which - // account it should use for backing up the data and notify GmsCore about the change by - // sending a broadcast to it. - String name = account != null ? account.name : null; - String type = account != null ? account.type: null; - Bundle appRestrictions = mDevicePolicyManager.getApplicationRestrictions( - mAdminComponentName, GMSCORE_PACKAGE); - if (name == null) { - appRestrictions.remove(MANDATORY_BACKUP_ACCOUNT_NAME_APP_RESTRICTION); - } else { - appRestrictions.putString(MANDATORY_BACKUP_ACCOUNT_NAME_APP_RESTRICTION, name); - } - if (type == null) { - appRestrictions.remove(MANDATORY_BACKUP_ACCOUNT_TYPE_APP_RESTRICTION); - } else { - appRestrictions.putString(MANDATORY_BACKUP_ACCOUNT_TYPE_APP_RESTRICTION, type); - } - mDevicePolicyManager.setApplicationRestrictions( - mAdminComponentName, GMSCORE_PACKAGE, appRestrictions); - Intent intent = new Intent(MANDATORY_BACKUP_ACCOUNT_CHANGED_ACTION); - intent.setPackage(GMSCORE_PACKAGE); - getContext().sendBroadcast(intent); - - // Update the UI for backup-related policies. - reloadEnableBackupServiceUi(); - reloadMandatoryBackupsUi(); - } - abstract class ManageLockTaskListCallback { public abstract void onPositiveButtonClicked(String[] lockTaskArray); } diff --git a/app/src/main/java/com/afwsamples/testdpc/policy/keyguard/PasswordBlacklistFragment.java b/app/src/main/java/com/afwsamples/testdpc/policy/keyguard/PasswordBlacklistFragment.java deleted file mode 100644 index d90710e8..00000000 --- a/app/src/main/java/com/afwsamples/testdpc/policy/keyguard/PasswordBlacklistFragment.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * 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 com.afwsamples.testdpc.policy.keyguard; - -import android.annotation.TargetApi; -import android.app.AlertDialog; -import android.app.admin.DevicePolicyManager; -import android.content.ComponentName; -import android.content.Context; -import android.os.Build; -import android.os.Bundle; -import android.support.annotation.StringRes; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.EditText; -import android.widget.SpinnerAdapter; -import android.widget.Toast; -import com.afwsamples.testdpc.DeviceAdminReceiver; -import com.afwsamples.testdpc.R; -import com.afwsamples.testdpc.common.BaseManageComponentFragment; -import com.afwsamples.testdpc.common.EditDeleteArrayAdapter; -import java.text.DateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -@TargetApi(28) -public class PasswordBlacklistFragment extends BaseManageComponentFragment - implements EditDeleteArrayAdapter.OnEditButtonClickListener { - - public PasswordConstraintsFragment passwordConstraintsFragment; - - private List mPasswordBlacklist = new ArrayList<>(); - private DevicePolicyManager mDevicePolicyManager; - private ComponentName mAdminComponent; - private EditDeleteArrayAdapter mPasswordBlacklistArrayAdapter; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mAdminComponent = DeviceAdminReceiver.getComponentName(getActivity()); - } - - public void setDpm(DevicePolicyManager dpm) { - mDevicePolicyManager = dpm; - } - - @Override - public View onCreateView(LayoutInflater layoutInflater, ViewGroup container, - Bundle savedInstanceState) { - View view = super.onCreateView(layoutInflater, container, savedInstanceState); - mManagedAppsSpinner.setVisibility(View.INVISIBLE); // We don't need the Spinner. - return view; - } - - @Override - public void onResume() { - super.onResume(); - getActivity().getActionBar().setTitle(R.string.password_blacklist_manager_title); - } - - @Override - public void onPause() { - super.onPause(); - if (passwordConstraintsFragment != null) { - // Blacklist has changed so updated the preferences - // TODO: there must be a neater way to do this but onResume isn't called on navigation - passwordConstraintsFragment.refreshBlacklistPreferences(); - } - } - - @Override - protected SpinnerAdapter createSpinnerAdapter() { - return null; // We don't need a spinner. - } - - @Override - protected void onSpinnerItemSelected(Void item) { - } - - @Override - protected BaseAdapter createListAdapter() { - mPasswordBlacklistArrayAdapter = new PasswordBlacklistEntryArrayAdapter( - getActivity(), mPasswordBlacklist, this); - return mPasswordBlacklistArrayAdapter; - } - - @Override - protected void resetConfig() { - mPasswordBlacklistArrayAdapter.clear(); - } - - @Override - protected void saveConfig() { - // Name the blacklist with the timestamp when it was set - final String name = DateFormat.getDateTimeInstance().format(new Date()); - if (!setBlacklist(mDevicePolicyManager, mAdminComponent, name, mPasswordBlacklist)) { - showToast(R.string.password_blacklist_save_failed); - return; - } - - showToast(R.string.password_blacklist_saved); - } - - @Override - protected void addNewRow() { - onEditButtonClick(null); - } - - @Override - protected void loadDefault() { - mPasswordBlacklistArrayAdapter.clear(); - } - - @Override - public void onEditButtonClick(final String existingEntry) { - View view = LayoutInflater.from(getActivity()).inflate(R.layout.simple_edittext, null); - final EditText input = view.findViewById(R.id.input); - if (existingEntry != null) { - input.setText(existingEntry); - } - - final AlertDialog dialog = new AlertDialog.Builder(getActivity()) - .setTitle(R.string.password_blacklist_add) - .setView(view) - .setPositiveButton(android.R.string.ok, null) - .setNegativeButton(android.R.string.cancel, null) - .create(); - dialog.setOnShowListener( - dialogInterface -> dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener( - okButtonView -> { - String password = input.getText().toString(); - if (TextUtils.isEmpty(password)) { - showToast(R.string.password_blacklist_add_empty_error); - return; - } - if (existingEntry != null) { - mPasswordBlacklistArrayAdapter.remove(existingEntry); - } - mPasswordBlacklistArrayAdapter.add(password); - dialog.dismiss(); - })); - dialog.show(); - } - - private void showToast(@StringRes int stringResId) { - Toast.makeText(getActivity(), stringResId, Toast.LENGTH_LONG).show(); - } - - static class PasswordBlacklistEntryArrayAdapter extends EditDeleteArrayAdapter { - PasswordBlacklistEntryArrayAdapter( - Context context, - List entries, - OnEditButtonClickListener onEditButtonClickListener) { - super(context, entries, onEditButtonClickListener, null); - } - - @Override - protected String getDisplayName(String entry) { - return entry; - } - } - - @TargetApi(28) - static boolean setBlacklist(DevicePolicyManager dpm, ComponentName admin, String name, - List blacklist) { - return dpm.setPasswordBlacklist(admin, name, blacklist); - } - - @TargetApi(28) - static String getBlacklistName(DevicePolicyManager dpm, ComponentName admin) { - return dpm.getPasswordBlacklistName(admin); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/afwsamples/testdpc/policy/keyguard/PasswordConstraintsFragment.java b/app/src/main/java/com/afwsamples/testdpc/policy/keyguard/PasswordConstraintsFragment.java index b1174ceb..96ec8292 100644 --- a/app/src/main/java/com/afwsamples/testdpc/policy/keyguard/PasswordConstraintsFragment.java +++ b/app/src/main/java/com/afwsamples/testdpc/policy/keyguard/PasswordConstraintsFragment.java @@ -16,15 +16,20 @@ package com.afwsamples.testdpc.policy.keyguard; -import android.app.Activity; -import android.app.Fragment; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; + +import static com.afwsamples.testdpc.common.preference.DpcPreferenceHelper.NO_CUSTOM_CONSTRIANT; + import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.net.Uri; import android.os.Bundle; -import android.support.v4.os.BuildCompat; import android.support.v7.preference.EditTextPreference; import android.support.v7.preference.ListPreference; import android.support.v7.preference.Preference; @@ -35,29 +40,14 @@ import com.afwsamples.testdpc.common.ProfileOrParentFragment; import com.afwsamples.testdpc.common.Util; import com.afwsamples.testdpc.common.preference.CustomConstraint; -import com.afwsamples.testdpc.common.preference.DpcPreference; import com.afwsamples.testdpc.common.preference.DpcPreferenceBase; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.text.DateFormat; import java.util.ArrayList; -import java.util.Date; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.TimeUnit; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; -import static com.afwsamples.testdpc.common.preference.DpcPreferenceHelper.NO_CUSTOM_CONSTRIANT; - /** * This fragment provides functionalities to set password constraint policies as a profile * or device owner. In the former case, it is also possible to set password constraints on @@ -74,12 +64,10 @@ *
  • {@link DevicePolicyManager#setPasswordMinimumSymbols(ComponentName, int)}
  • *
  • {@link DevicePolicyManager#setPasswordMinimumNonLetter(ComponentName, int)}
  • *
  • {@link DevicePolicyManager#setPasswordHistoryLength(ComponentName, int)}
  • - *
  • {@link DevicePolicyManager#setPasswordBlacklist(ComponentName, String, List)}
  • * */ public final class PasswordConstraintsFragment extends ProfileOrParentFragment implements - Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener { - private static final int READ_BLACKLIST_FILE_CODE = 42; + Preference.OnPreferenceChangeListener { private DpcPreferenceBase mMinLength; private DpcPreferenceBase mMinLetters; @@ -88,7 +76,6 @@ public final class PasswordConstraintsFragment extends ProfileOrParentFragment i private DpcPreferenceBase mMinUpper; private DpcPreferenceBase mMinSymbols; private DpcPreferenceBase mMinNonLetter; - private DpcPreference mClearBlacklist; public static class Container extends ProfileOrParentFragment.Container { @Override @@ -111,10 +98,6 @@ abstract static class Keys { final static String MIN_UPPERCASE = "password_min_uppercase"; final static String MIN_SYMBOLS = "password_min_symbols"; final static String MIN_NONLETTER = "password_min_nonletter"; - - final static String SET_PASSWORD_BLACKLIST_KEY = "set_password_blacklist"; - final static String LOAD_PASSWORD_BLACKLIST_KEY = "load_password_blacklist"; - final static String CLEAR_PASSWORD_BLACKLIST_KEY = "clear_password_blacklist"; } private static final TreeMap PASSWORD_QUALITIES = new TreeMap<>(); @@ -169,7 +152,6 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { mMinUpper = (DpcPreferenceBase) findPreference(Keys.MIN_UPPERCASE); mMinSymbols = (DpcPreferenceBase) findPreference(Keys.MIN_SYMBOLS); mMinNonLetter = (DpcPreferenceBase) findPreference(Keys.MIN_NONLETTER); - mClearBlacklist = (DpcPreference) findPreference(Keys.CLEAR_PASSWORD_BLACKLIST_KEY); // Populate password quality settings - messy because the only API for this requires two // separate String[]s. @@ -199,10 +181,6 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { setup(Keys.MIN_SYMBOLS, getDpm().getPasswordMinimumSymbols(getAdmin())); setup(Keys.MIN_NONLETTER, getDpm().getPasswordMinimumNonLetter(getAdmin())); - findPreference(Keys.SET_PASSWORD_BLACKLIST_KEY).setOnPreferenceClickListener(this); - findPreference(Keys.LOAD_PASSWORD_BLACKLIST_KEY).setOnPreferenceClickListener(this); - mClearBlacklist.setOnPreferenceClickListener(this); - setPreferencesConstraint(); } @@ -212,9 +190,6 @@ public void onResume() { // Settings that may have been changed by other users need updating. updateExpirationTimes(); - if (BuildCompat.isAtLeastP()) { - refreshBlacklistPreferences(); - } } @Override @@ -282,43 +257,6 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { return true; } - public boolean onPreferenceClick(Preference preference) { - String key = preference.getKey(); - switch (key) { - case Keys.SET_PASSWORD_BLACKLIST_KEY: - showPasswordBlacklistFragment(); - return true; - case Keys.LOAD_PASSWORD_BLACKLIST_KEY: - Util.showFilePicker(this, "text/plain", READ_BLACKLIST_FILE_CODE); - return true; - case Keys.CLEAR_PASSWORD_BLACKLIST_KEY: - PasswordBlacklistFragment.setBlacklist(getDpm(), getAdmin(), null, null); - refreshBlacklistPreferences(); - return true; - default: - break; - } - - return false; - } - - private void showPasswordBlacklistFragment() { - final PasswordBlacklistFragment fragment = new PasswordBlacklistFragment(); - - fragment.passwordConstraintsFragment = this; - fragment.setDpm(getDpm()); - - Fragment containerFragment = getParentFragment(); - if (containerFragment == null) { - containerFragment = this; - } - containerFragment.getFragmentManager().beginTransaction() - .addToBackStack(PasswordBlacklistFragment.class.getName()) - .hide(containerFragment) - .add(R.id.container, fragment) - .commit(); - } - /** * Enable and disable password constraint preferences based on the current password quality. */ @@ -340,10 +278,6 @@ private void setPreferencesConstraint() { mMinUpper.setCustomConstraint(constraint); mMinSymbols.setCustomConstraint(constraint); mMinNonLetter.setCustomConstraint(constraint); - - mClearBlacklist.setCustomConstraint( - () -> PasswordBlacklistFragment.getBlacklistName(getDpm(), getAdmin()) != null - ? NO_CUSTOM_CONSTRIANT : R.string.password_blacklist_no_blacklist); } private void refreshPreferences() { @@ -356,10 +290,6 @@ private void refreshPreferences() { mMinNonLetter.refreshEnabledState(); } - void refreshBlacklistPreferences() { - mClearBlacklist.refreshEnabledState(); - mClearBlacklist.setSummary(PasswordBlacklistFragment.getBlacklistName(getDpm(), getAdmin())); - } /** * Set an initial value. Updates the summary to match. @@ -396,39 +326,4 @@ private void updateExpirationTimes() { byAdmin.setSummary(Util.formatTimestamp(getDpm().getPasswordExpiration(getAdmin()))); byAll.setSummary(Util.formatTimestamp(getDpm().getPasswordExpiration(null))); } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent resultData) { - if (requestCode == READ_BLACKLIST_FILE_CODE && resultCode == Activity.RESULT_OK) { - if (resultData != null) { - final Uri uri = resultData.getData(); - final List blacklist; - try { - blacklist = readTextFileLines(uri); - } catch (IOException e) { - Toast.makeText(getActivity(), R.string.password_blacklist_load_failed, - Toast.LENGTH_LONG).show(); - return; - } - final String name = DateFormat.getDateTimeInstance().format(new Date()); - if (!PasswordBlacklistFragment.setBlacklist(getDpm(), getAdmin(), name, - blacklist)) { - Toast.makeText(getActivity(), R.string.password_blacklist_save_failed, - Toast.LENGTH_LONG).show(); - } - } - } - } - - private List readTextFileLines(Uri uri) throws IOException { - final List lines = new ArrayList<>(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader( - getActivity().getApplicationContext().getContentResolver().openInputStream(uri)))) { - String line; - while ((line = reader.readLine()) != null) { - lines.add(line); - } - } - return lines; - } } diff --git a/app/src/main/java/com/afwsamples/testdpc/policy/locktask/SetLockTaskFeaturesFragment.java b/app/src/main/java/com/afwsamples/testdpc/policy/locktask/SetLockTaskFeaturesFragment.java index 7f31381c..5d6684e6 100644 --- a/app/src/main/java/com/afwsamples/testdpc/policy/locktask/SetLockTaskFeaturesFragment.java +++ b/app/src/main/java/com/afwsamples/testdpc/policy/locktask/SetLockTaskFeaturesFragment.java @@ -20,6 +20,7 @@ import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME; import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD; import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS; +import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW; import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO; import android.annotation.TargetApi; @@ -60,18 +61,6 @@ public class SetLockTaskFeaturesFragment private static final String KEY_GLOBAL_ACTIONS = "lock_task_feature_global_actions"; private static final String KEY_KEYGUARD = "lock_task_feature_keyguard"; - private static final int LOCK_TASK_FEATURE_OVERVIEW; - static { - int flag = 1 << 3; - try { - flag = ReflectionUtil.intConstant( - DevicePolicyManager.class, "LOCK_TASK_FEATURE_OVERVIEW"); - } catch (ReflectionUtil.ReflectionIsTemporaryException e) { - } finally { - LOCK_TASK_FEATURE_OVERVIEW = flag; - } - } - /** Maps from preference keys to {@link DevicePolicyManager#setLockTaskFeatures}'s flags. */ private static final ArrayMap FEATURE_FLAGS = new ArrayMap<>(); static { @@ -129,8 +118,8 @@ public boolean onPreferenceChange(Preference pref, Object val) { ? flagsBefore | FEATURE_FLAGS.get(key) : flagsBefore & ~FEATURE_FLAGS.get(key); if ((flagsAfter & LOCK_TASK_FEATURE_HOME) == 0) { - // Disable OVERVIEW when HOME is disabled - flagsAfter &= ~LOCK_TASK_FEATURE_OVERVIEW; + // Disable OVERVIEW and NOTIFICATION when HOME is disabled + flagsAfter &= ~(LOCK_TASK_FEATURE_OVERVIEW | LOCK_TASK_FEATURE_NOTIFICATIONS); } if (flagsAfter != flagsBefore) { Log.i(TAG, "LockTask feature flags changing from 0x" + Integer.toHexString(flagsBefore) @@ -155,8 +144,13 @@ public boolean isAvailable(Context context) { } private void enforceEnablingRestrictions(int enabledFeatures) { - DpcSwitchPreference pref = (DpcSwitchPreference) findPreference(KEY_OVERVIEW); - pref.setEnabled((enabledFeatures & LOCK_TASK_FEATURE_HOME) != 0); + boolean isHomeEnabled = (enabledFeatures & LOCK_TASK_FEATURE_HOME) != 0; + setPrefEnabledState((DpcSwitchPreference) findPreference(KEY_OVERVIEW), isHomeEnabled); + setPrefEnabledState((DpcSwitchPreference) findPreference(KEY_NOTIFICATIONS), isHomeEnabled); + } + + private void setPrefEnabledState(DpcSwitchPreference pref, boolean enabled) { + pref.setEnabled(enabled); if (!pref.isEnabled() && pref.isChecked()) { pref.setChecked(false); } diff --git a/app/src/main/java/com/afwsamples/testdpc/policy/systemupdatepolicy/SystemUpdatePolicyFragment.java b/app/src/main/java/com/afwsamples/testdpc/policy/systemupdatepolicy/SystemUpdatePolicyFragment.java index 275782e9..2a3b8038 100644 --- a/app/src/main/java/com/afwsamples/testdpc/policy/systemupdatepolicy/SystemUpdatePolicyFragment.java +++ b/app/src/main/java/com/afwsamples/testdpc/policy/systemupdatepolicy/SystemUpdatePolicyFragment.java @@ -21,6 +21,7 @@ import android.app.Fragment; import android.app.TimePickerDialog; import android.app.admin.DevicePolicyManager; +import android.app.admin.FreezePeriod; import android.app.admin.SystemUpdatePolicy; import android.content.Context; import android.os.Build; @@ -43,6 +44,7 @@ import com.afwsamples.testdpc.R; import java.time.LocalDate; +import java.time.MonthDay; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; @@ -62,18 +64,20 @@ public class SystemUpdatePolicyFragment extends Fragment implements View.OnClick @RequiresApi(api = Build.VERSION_CODES.O) static class Period { - LocalDate mStart; - LocalDate mEnd; + MonthDay mStart; + MonthDay mEnd; - public Period(Integer start, Integer end) { - int currentYear = LocalDate.now().getYear(); - mStart = LocalDate.ofYearDay(2001, start).withYear(currentYear); - mEnd = LocalDate.ofYearDay(2001, end).withYear(end >= start ? currentYear : currentYear + 1); + public Period() { } - public Period(LocalDate startDate, LocalDate endDate) { - mStart = startDate; - mEnd = endDate; + public Period(MonthDay start, MonthDay end) { + mStart = start; + mEnd = end; + } + + public void set(LocalDate startDate, LocalDate endDate) { + mStart = MonthDay.of(startDate.getMonth(), startDate.getDayOfMonth()); + mEnd = MonthDay.of(endDate.getMonth(), endDate.getDayOfMonth()); } @Override @@ -82,9 +86,17 @@ public String toString() { return mStart.format(formatter) + " - " + mEnd.format(formatter); } - public Pair toIntegers() { - return new Pair<>(mStart.withYear(2001).getDayOfYear(), - mEnd.withYear(2001).getDayOfYear()); + public LocalDate getStartDate() { + return mStart.atYear(LocalDate.now().getYear()); + } + + public LocalDate getEndDate() { + return mEnd.atYear(LocalDate.now().getYear()); + } + + @TargetApi(28) + public FreezePeriod toFreezePeriod() { + return new FreezePeriod(mStart, mEnd); } } @@ -131,10 +143,9 @@ public View getView(int position, View convertView, ViewGroup parent) { textView.setOnClickListener(view -> { final Period period = (Period) view.getTag(); promptToSetFreezePeriod((LocalDate startDate, LocalDate endDate) -> { - period.mStart = startDate; - period.mEnd = endDate; + period.set(startDate, endDate); mFreezePeriodAdapter.notifyDataSetChanged(); - }, period.mStart, period.mEnd); + }, period.getStartDate(), period.getEndDate()); }); View deleteButton = convertView.findViewById(R.id.delete_period); deleteButton.setTag(mData.get(position)); @@ -210,7 +221,8 @@ public void onClick(View v) { break; case R.id.system_update_policy_btn_add_period: promptToSetFreezePeriod((LocalDate startDate, LocalDate endDate) -> { - Period period = new Period(startDate, endDate); + Period period = new Period(); + period.set(startDate, endDate); mFreezePeriods.add(period); mFreezePeriodAdapter.notifyDataSetChanged(); }, LocalDate.now(), LocalDate.now()); @@ -269,21 +281,22 @@ private boolean setSystemUpdatePolicy() { default: newPolicy = null; } - if (BuildCompat.isAtLeastP() && newPolicy != null && mFreezePeriods.size() != 0) { - List> periods = new ArrayList<>(mFreezePeriods.size()); - for (Period p : mFreezePeriods) { - periods.add(p.toIntegers()); - } - try { + + try { + if (BuildCompat.isAtLeastP() && newPolicy != null && mFreezePeriods.size() != 0) { + final List periods = new ArrayList<>(mFreezePeriods.size()); + for (Period p : mFreezePeriods) { + periods.add(p.toFreezePeriod()); + } newPolicy.setFreezePeriods(periods); - mDpm.setSystemUpdatePolicy(DeviceAdminReceiver.getComponentName(getActivity()), - newPolicy); - Toast.makeText(getContext(), "Policy set successfully", Toast.LENGTH_LONG).show(); - return true; - } catch (IllegalArgumentException e) { - Toast.makeText(getContext(), "Failed to set system update policy: " + e.getMessage(), - Toast.LENGTH_LONG).show(); } + mDpm.setSystemUpdatePolicy(DeviceAdminReceiver.getComponentName(getActivity()), + newPolicy); + Toast.makeText(getContext(), "Policy set successfully", Toast.LENGTH_LONG).show(); + return true; + } catch (IllegalArgumentException e) { + Toast.makeText(getContext(), "Failed to set system update policy: " + e.getMessage(), + Toast.LENGTH_LONG).show(); } return false; } @@ -332,10 +345,10 @@ private void reloadSystemUpdatePolicy() { break; } if (BuildCompat.isAtLeastP()) { - List> freezePeriods = policy.getFreezePeriods(); + List freezePeriods = policy.getFreezePeriods(); mFreezePeriods.clear(); - for (Pair period : freezePeriods) { - Period p = new Period(period.first, period.second); + for (FreezePeriod period : freezePeriods) { + Period p = new Period(period.getStart(), period.getEnd()); mFreezePeriods.add(p); } mFreezePeriodAdapter.notifyDataSetChanged(); diff --git a/app/src/main/java/com/afwsamples/testdpc/policy/wifimanagement/WifiConfigCreationDialog.java b/app/src/main/java/com/afwsamples/testdpc/policy/wifimanagement/WifiConfigCreationDialog.java index 372fee15..161f97fd 100644 --- a/app/src/main/java/com/afwsamples/testdpc/policy/wifimanagement/WifiConfigCreationDialog.java +++ b/app/src/main/java/com/afwsamples/testdpc/policy/wifimanagement/WifiConfigCreationDialog.java @@ -149,7 +149,6 @@ public void onDismiss(DialogInterface dialog) { if (mListener != null) { mListener.onDismiss(); } - dismiss(); } @Override @@ -157,7 +156,6 @@ public void onCancel(DialogInterface dialog) { if (mListener != null) { mListener.onCancel(); } - dismiss(); } private void initialize() { 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 be4de37b..53b93704 100644 --- a/app/src/main/java/com/afwsamples/testdpc/provision/PostProvisioningTask.java +++ b/app/src/main/java/com/afwsamples/testdpc/provision/PostProvisioningTask.java @@ -16,10 +16,6 @@ package com.afwsamples.testdpc.provision; -import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE; -import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED; -import static com.afwsamples.testdpc.DeviceAdminReceiver.getComponentName; - import android.accounts.Account; import android.accounts.AccountManager; import android.annotation.TargetApi; @@ -38,8 +34,7 @@ import com.afwsamples.testdpc.AddAccountActivity; import com.afwsamples.testdpc.DeviceAdminReceiver; -import com.afwsamples.testdpc.EnableDeviceOwnerActivity; -import com.afwsamples.testdpc.EnableProfileActivity; +import com.afwsamples.testdpc.FinalizeActivity; import com.afwsamples.testdpc.common.LaunchIntentUtil; import com.afwsamples.testdpc.common.Util; import com.afwsamples.testdpc.cosu.EnableCosuActivity; @@ -48,6 +43,10 @@ import java.util.Collections; import java.util.List; +import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE; +import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED; +import static com.afwsamples.testdpc.DeviceAdminReceiver.getComponentName; + /** * Task executed after provisioning is done indicated by either the * {@link DevicePolicyManager#ACTION_PROVISIONING_SUCCESSFUL} activity intent or the @@ -140,13 +139,11 @@ public Intent getPostProvisioningLaunchIntent(Intent intent) { return null; } - if (isProfileOwner) { - launch = new Intent(mContext, EnableProfileActivity.class); - } else if (cosuLaunch) { + if (cosuLaunch) { launch = new Intent(mContext, EnableCosuActivity.class); launch.putExtra(EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE, extras); } else { - launch = new Intent(mContext, EnableDeviceOwnerActivity.class); + launch = new Intent(mContext, FinalizeActivity.class); } if (synchronousAuthLaunch) { diff --git a/app/src/main/java/com/afwsamples/testdpc/provision/ProvisioningUtil.java b/app/src/main/java/com/afwsamples/testdpc/provision/ProvisioningUtil.java index 8eb742e6..85c85b64 100644 --- a/app/src/main/java/com/afwsamples/testdpc/provision/ProvisioningUtil.java +++ b/app/src/main/java/com/afwsamples/testdpc/provision/ProvisioningUtil.java @@ -21,12 +21,10 @@ import android.content.Context; import com.afwsamples.testdpc.DeviceAdminReceiver; -import com.afwsamples.testdpc.FirstAccountReadyBroadcastReceiver; import com.afwsamples.testdpc.R; public class ProvisioningUtil { public static void enableProfile(Context context) { - FirstAccountReadyBroadcastReceiver.cancelFirstAccountReadyTimeoutAlarm(context); DevicePolicyManager manager = (DevicePolicyManager) context.getSystemService( Context.DEVICE_POLICY_SERVICE); ComponentName componentName = DeviceAdminReceiver.getComponentName(context); diff --git a/app/src/main/java/com/afwsamples/testdpc/safetynet/SafetyNetFragment.java b/app/src/main/java/com/afwsamples/testdpc/safetynet/SafetyNetFragment.java deleted file mode 100644 index 9829ef03..00000000 --- a/app/src/main/java/com/afwsamples/testdpc/safetynet/SafetyNetFragment.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright 2016 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.afwsamples.testdpc.safetynet; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.Bundle; -import android.support.annotation.ColorInt; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.content.ContextCompat; -import android.text.method.ScrollingMovementMethod; -import android.util.Base64; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; - -import com.afwsamples.testdpc.R; -import com.google.android.gms.common.ConnectionResult; -import com.google.android.gms.common.api.GoogleApiClient; -import com.google.android.gms.common.api.ResultCallbacks; -import com.google.android.gms.common.api.Status; -import com.google.android.gms.safetynet.SafetyNet; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.security.SecureRandom; - -import static com.google.android.gms.safetynet.SafetyNetApi.AttestationResult; - -/** - * Demonstrate how to use SafetyNet API to check device compatibility. - * Please notice that you should verifying the payload in your server. - * For more details, please check http://developer.android.com/training/safetynet/index.html. - */ -public class SafetyNetFragment extends DialogFragment implements - GoogleApiClient.ConnectionCallbacks, - GoogleApiClient.OnConnectionFailedListener { - private GoogleApiClient mGoogleApiClient; - private TextView mMessageView; - private @ColorInt int BLACK, DARK_RED; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - // Kick start the checking - mGoogleApiClient = buildGoogleApiClient(); - } - - @Override - public void onStart() { - super.onStart(); - updateMessageView(R.string.safetynet_running, false); - mGoogleApiClient.connect(); - } - - @Override - public void onStop() { - super.onStop(); - mGoogleApiClient.disconnect(); - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - BLACK = ContextCompat.getColor(getActivity(), R.color.text_black); - DARK_RED = ContextCompat.getColor(getActivity(), R.color.dark_red); - LayoutInflater inflater = LayoutInflater.from(getActivity()); - View rootView = inflater.inflate(R.layout.safety_net_attest_dialog, null); - mMessageView = (TextView) rootView.findViewById(R.id.message_view); - // Show scrollbar in textview. - mMessageView.setMovementMethod(new ScrollingMovementMethod()); - return new AlertDialog.Builder(getActivity()) - .setView(rootView) - .setTitle(R.string.safetynet_dialog_title) - .setNeutralButton(android.R.string.ok, null) - .create(); - } - - @Override - public void onConnected(@Nullable Bundle bundle) { - if (hasInternetConnection()) { - runSaftyNetTest(); - } else { - updateMessageView(R.string.safetynet_fail_reason_no_internet, true); - } - } - - @Override - public void onConnectionSuspended(int i) { - updateMessageView(R.string.cancel_safetynet_msg, true); - } - - @Override - public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { - if (connectionResult.getErrorCode() == ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED) { - updateMessageView(R.string.safetynet_fail_reason_gmscore_upgrade, true); - } else { - updateMessageView(getString(R.string.safetynet_fail_reason_error_code, - connectionResult.getErrorCode()), true); - } - } - - private GoogleApiClient buildGoogleApiClient() { - return new GoogleApiClient.Builder(getActivity()) - .addApi(SafetyNet.API) - .addConnectionCallbacks(this) - .addOnConnectionFailedListener(this) - .build(); - } - - /** - * For simplicity, we generate the nonce in the client. However, it should be generated on the - * server for anti-replay protection. - */ - private byte[] generateNonce() { - byte[] nonce = new byte[32]; - SecureRandom secureRandom = new SecureRandom(); - secureRandom.nextBytes(nonce); - return nonce; - } - - private void runSaftyNetTest() { - final byte[] nonce = generateNonce(); - SafetyNet.SafetyNetApi.attest(mGoogleApiClient, nonce) - .setResultCallback(new ResultCallbacks() { - @Override - public void onSuccess(@NonNull AttestationResult attestationResult) { - if (isDetached()) { - return; - } - final String jws = attestationResult.getJwsResult(); - try { - final JSONObject jsonObject = retrievePayloadFromJws(jws); - final String jsonString = jsonObject.toString(4); - final String verifyOnServerString - = getString(R.string.safetynet_verify_on_server); - updateMessageView(verifyOnServerString + "\n" + jsonString, false); - } catch (JSONException ex) { - updateMessageView(R.string.safetynet_fail_reason_invalid_jws, true); - } - } - - @Override - public void onFailure(@NonNull Status status) { - if (isDetached()) { - return; - } - updateMessageView(R.string.safetynet_fail_to_run_api, true); - } - }); - } - - private void updateMessageView(int message, boolean isError) { - updateMessageView(getString(message), isError); - } - - private void updateMessageView(String message, boolean isError) { - mMessageView.setText(message); - mMessageView.setTextColor((isError) ? DARK_RED : BLACK); - } - - private boolean hasInternetConnection() { - ConnectivityManager cm = - (ConnectivityManager) getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); - NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); - return activeNetwork != null && activeNetwork.isConnected(); - } - - private static JSONObject retrievePayloadFromJws(String jws) throws JSONException { - String[] parts = jws.split("\\."); - if (parts.length != 3) { - throw new JSONException("Invalid JWS"); - } - return new JSONObject(new String(Base64.decode(parts[1], Base64.URL_SAFE))); - } -} diff --git a/app/src/main/java/com/afwsamples/testdpc/search/PreferenceXmlUtil.java b/app/src/main/java/com/afwsamples/testdpc/search/PreferenceXmlUtil.java index 0434efda..9eab71f2 100644 --- a/app/src/main/java/com/afwsamples/testdpc/search/PreferenceXmlUtil.java +++ b/app/src/main/java/com/afwsamples/testdpc/search/PreferenceXmlUtil.java @@ -5,38 +5,29 @@ import android.util.AttributeSet; import android.util.TypedValue; -import java.lang.reflect.Field; - /** * Util class to retrieve some values of attributes in preference xml. * To achieve this, we need to: - * 1. Obtain the array android.R$styleable.Preference through reflection. - * Cache is introduced to reduce the performance overhead introduced by reflection. - * 2. Obtain the resource id of certain attributes that we care such as title and key using - * reflection. Again, cache is introduced. - * 3. Obtain the value of those attribute {@link TypedArray#peekValue(int)}. + * 1. Obtain the resource id of certain attributes that we care such as title and key. + * 2. Obtain the value of those attribute {@link TypedArray#peekValue(int)}. */ public class PreferenceXmlUtil { - private static Integer sPreferenceTitleId; - private static Integer sPreferenceKeyId; - private static int[] sPreferenceStyleArray; public static String getDataTitle(Context context, AttributeSet attrs) throws ReflectiveOperationException { - return getData(context, attrs, getPreferenceTitleId()); + return getData(context, attrs, android.R.attr.title); } public static String getDataKey(Context context, AttributeSet attrs) throws ReflectiveOperationException { - return getData(context, attrs, getPreferenceKeyId()); + return getData(context, attrs, android.R.attr.key); } - private static String getData(Context context, AttributeSet set, int resId) + private static String getData(Context context, AttributeSet set, int attribute) throws ReflectiveOperationException { - int[] attrs = getPreferenceStyleArray(); - final TypedArray sa = context.obtainStyledAttributes(set, attrs); + final TypedArray sa = context.obtainStyledAttributes(set, new int[] {attribute}); try { - final TypedValue tv = sa.peekValue(resId); + final TypedValue tv = sa.peekValue(0); CharSequence data = null; if (tv != null && tv.type == TypedValue.TYPE_STRING) { if (tv.resourceId != 0) { @@ -50,35 +41,4 @@ private static String getData(Context context, AttributeSet set, int resId) sa.recycle(); } } - - private static int getPreferenceTitleId() throws ReflectiveOperationException { - if (sPreferenceTitleId == null) { - sPreferenceTitleId = getStyleableId("Preference_title"); - } - return sPreferenceTitleId; - } - - private static int getPreferenceKeyId() throws ReflectiveOperationException { - if (sPreferenceKeyId == null) { - sPreferenceKeyId = getStyleableId("Preference_key"); - } - return sPreferenceKeyId; - } - - private static int[] getPreferenceStyleArray() throws ReflectiveOperationException { - if (sPreferenceStyleArray == null) { - sPreferenceStyleArray = getStyleableArray("Preference"); - } - return sPreferenceStyleArray; - } - - private static int getStyleableId(String name) throws ReflectiveOperationException { - Field field = Class.forName("android.R$styleable").getDeclaredField(name); - return (int) field.get(null); - } - - private static final int[] getStyleableArray(String name) throws ReflectiveOperationException { - Field field = Class.forName("android.R$styleable").getDeclaredField(name); - return (int[]) field.get(null); - } } diff --git a/app/src/main/res/drawable/ic_enterprise_blue.xml b/app/src/main/res/drawable/ic_enterprise_blue.xml new file mode 100644 index 00000000..da7057a1 --- /dev/null +++ b/app/src/main/res/drawable/ic_enterprise_blue.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_add_account.xml b/app/src/main/res/layout/activity_add_account.xml index c0bb0190..18e5e395 100644 --- a/app/src/main/res/layout/activity_add_account.xml +++ b/app/src/main/res/layout/activity_add_account.xml @@ -1,5 +1,4 @@ - - - + + + android:orientation="vertical"> - + + - + + + + + android:text="@string/add_account_with_name" /> - - - - + android:text="@string/add_account_skip" /> - + diff --git a/app/src/main/res/layout/enable_cosu_activity.xml b/app/src/main/res/layout/enable_cosu_activity.xml index 6cdbd770..077fe187 100644 --- a/app/src/main/res/layout/enable_cosu_activity.xml +++ b/app/src/main/res/layout/enable_cosu_activity.xml @@ -21,7 +21,6 @@ android:id="@+id/setup_wizard_layout" android:layout_width="match_parent" android:layout_height="match_parent" - style="@style/SetupIllustrationTheme" app:suwHeaderText="@string/setup_cosu"> - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/enable_profile_activity.xml b/app/src/main/res/layout/enable_profile_activity.xml deleted file mode 100644 index cb552cdb..00000000 --- a/app/src/main/res/layout/enable_profile_activity.xml +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/finalize_activity.xml b/app/src/main/res/layout/finalize_activity.xml new file mode 100644 index 00000000..59328ca3 --- /dev/null +++ b/app/src/main/res/layout/finalize_activity.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/insert_apn.xml b/app/src/main/res/layout/insert_apn.xml index c70f4b89..7e0e9bc6 100644 --- a/app/src/main/res/layout/insert_apn.xml +++ b/app/src/main/res/layout/insert_apn.xml @@ -186,12 +186,11 @@ android:layout_height="wrap_content" android:text="@string/apn_auth_type" style="@style/networking_item_label"/> - + - + - + - + - + diff --git a/app/src/main/res/layout/next_footer.xml b/app/src/main/res/layout/next_footer.xml new file mode 100644 index 00000000..467957ce --- /dev/null +++ b/app/src/main/res/layout/next_footer.xml @@ -0,0 +1,34 @@ + + + + + + +