├── .bazelversion ├── .github └── workflows │ └── build.yml ├── .gitignore ├── BUILD ├── CONTRIB ├── LICENSE ├── MODULE.bazel ├── PRIVACY ├── README.md ├── WORKSPACE ├── build.sh ├── qrcode.png ├── scripts └── ij.bazelproject ├── setupcompat.BUILD ├── setupdesign.BUILD └── src ├── main ├── AndroidManifest.xml ├── AndroidManifestDebug.xml ├── aidl │ ├── android │ │ └── content │ │ │ └── res │ │ │ └── AssetFileDescriptor.aidl │ └── com │ │ └── afwsamples │ │ └── testdpc │ │ └── comp │ │ ├── IDeviceOwnerService.aidl │ │ └── IProfileOwnerService.aidl ├── java │ └── com │ │ └── afwsamples │ │ └── testdpc │ │ ├── AddAccountActivity.java │ │ ├── BootReceiver.java │ │ ├── CommonReceiverOperations.java │ │ ├── CrossProfileAppsAllowlistFragment.java │ │ ├── CrossProfileAppsFragment.java │ │ ├── DelegatedAdminReceiver.java │ │ ├── DeviceAdminReceiver.java │ │ ├── DeviceAdminService.java │ │ ├── DevicePolicyManagerGateway.java │ │ ├── DevicePolicyManagerGatewayImpl.java │ │ ├── FinalizeActivity.java │ │ ├── PackageMonitorReceiver.java │ │ ├── PolicyManagementActivity.java │ │ ├── SetupManagementActivity.java │ │ ├── SetupManagementFragment.java │ │ ├── ShellCommand.java │ │ ├── UserIconContentProvider.java │ │ ├── WorkPolicyInfoActivity.java │ │ ├── common │ │ ├── AccountArrayAdapter.java │ │ ├── AppInfoArrayAdapter.java │ │ ├── AppInfoSpinnerAdapter.java │ │ ├── BaseManageComponentFragment.java │ │ ├── BaseSearchablePolicyPreferenceFragment.java │ │ ├── BundleUtil.java │ │ ├── CertificateUtil.java │ │ ├── ColorPicker.java │ │ ├── Dumpable.java │ │ ├── DumpableActivity.java │ │ ├── EditDeleteArrayAdapter.java │ │ ├── IntentOrIntentFilterFragment.java │ │ ├── LaunchIntentUtil.java │ │ ├── ManageAppFragment.java │ │ ├── ManageResolveInfoFragment.java │ │ ├── MediaDisplayFragment.java │ │ ├── NotificationUtil.java │ │ ├── OnBackPressedHandler.java │ │ ├── PackageInstallationUtils.java │ │ ├── PermissionsHelper.java │ │ ├── ProfileOrParentFragment.java │ │ ├── ProvisioningStateUtil.java │ │ ├── ReflectionUtil.java │ │ ├── ResolveInfoSpinnerAdapter.java │ │ ├── RestrictionManagerCompat.java │ │ ├── SelectAppFragment.java │ │ ├── StringArrayTypeInputAdapter.java │ │ ├── ThemeUtil.java │ │ ├── ToggleComponentsArrayAdapter.java │ │ ├── UserArrayAdapter.java │ │ ├── Util.java │ │ ├── keyvaluepair │ │ │ ├── KeyValueBundleArrayFragment.java │ │ │ ├── KeyValueBundleFragment.java │ │ │ └── KeyValuePairDialogFragment.java │ │ └── preference │ │ │ ├── CustomConstraint.java │ │ │ ├── DpcEditTextPreference.java │ │ │ ├── DpcListPreference.java │ │ │ ├── DpcPreference.java │ │ │ ├── DpcPreferenceBase.java │ │ │ ├── DpcPreferenceHelper.java │ │ │ └── DpcSwitchPreference.java │ │ ├── comp │ │ ├── BindDeviceAdminFragment.java │ │ ├── BindDeviceAdminServiceHelper.java │ │ ├── DeviceOwnerService.java │ │ ├── OnServiceConnectedListener.java │ │ ├── ProfileOwnerService.java │ │ └── ServiceInterfaceConverter.java │ │ ├── cosu │ │ ├── CosuConfig.java │ │ ├── CosuUtils.java │ │ └── EnableCosuActivity.java │ │ ├── feedback │ │ └── AppStatesService.java │ │ ├── policy │ │ ├── AvailableComponentsInfoArrayAdapter.java │ │ ├── BaseStringItemsFragment.java │ │ ├── CrossProfileCalendarFragment.java │ │ ├── EnterpriseSliceFragment.java │ │ ├── EsimControlFragment.java │ │ ├── FactoryResetProtectionPolicyFragment.java │ │ ├── GetAvailableComponentsTask.java │ │ ├── ManageAffiliationIdsFragment.java │ │ ├── ManageKeepUninstalledPackagesFragment.java │ │ ├── MeteredDataRestrictionInfoAdapter.java │ │ ├── NetworkLogsFragment.java │ │ ├── OverrideApnFragment.java │ │ ├── PolicyManagementFragment.java │ │ ├── SecurityLogsFragment.java │ │ ├── SetSupportMessageFragment.java │ │ ├── SetUserSessionMessageFragment.java │ │ ├── UserRestriction.java │ │ ├── UserRestrictionsDisplayFragment.java │ │ ├── UserRestrictionsParentDisplayFragment.java │ │ ├── blockuninstallation │ │ │ └── BlockUninstallationInfoArrayAdapter.java │ │ ├── certificate │ │ │ └── DelegatedCertInstallerFragment.java │ │ ├── keyguard │ │ │ ├── LockScreenPolicyFragment.java │ │ │ ├── PasswordConstraintsFragment.java │ │ │ └── SetTrustAgentConfigFragment.java │ │ ├── keymanagement │ │ │ ├── GenerateKeyAndCertificateTask.java │ │ │ ├── KeyGenerationParameters.java │ │ │ ├── ShowToastCallback.java │ │ │ └── SignAndVerifyTask.java │ │ ├── locktask │ │ │ ├── KioskModeActivity.java │ │ │ ├── LockTaskAppInfoArrayAdapter.java │ │ │ └── SetLockTaskFeaturesFragment.java │ │ ├── networking │ │ │ ├── AlwaysOnVpnFragment.java │ │ │ ├── NetworkUsageStatsFragment.java │ │ │ ├── PrivateDnsModeFragment.java │ │ │ └── SetPrivateDnsTask.java │ │ ├── resetpassword │ │ │ ├── ResetPasswordService.java │ │ │ └── ResetPasswordWithTokenFragment.java │ │ ├── systemupdatepolicy │ │ │ └── SystemUpdatePolicyFragment.java │ │ ├── utils │ │ │ ├── Asn1Utils.java │ │ │ ├── Attestation.java │ │ │ ├── AttestationApplicationId.java │ │ │ ├── AttestationPackageInfo.java │ │ │ ├── AuthorizationList.java │ │ │ ├── CertificateUtils.java │ │ │ └── RootOfTrust.java │ │ └── wifimanagement │ │ │ ├── WifiConfigCreationDialog.java │ │ │ ├── WifiConfigUtil.java │ │ │ ├── WifiEapTlsCreateDialogFragment.java │ │ │ └── WifiModificationFragment.java │ │ ├── profilepolicy │ │ ├── ProfilePolicyManagementFragment.java │ │ ├── addsystemapps │ │ │ └── EnableSystemAppsByIntentFragment.java │ │ ├── apprestrictions │ │ │ ├── AppRestrictionsManagingPackageFragment.java │ │ │ ├── AppRestrictionsProxy.java │ │ │ ├── AppRestrictionsProxyHandler.java │ │ │ └── ManagedConfigurationsFragment.java │ │ ├── crossprofileintentfilter │ │ │ └── AddCrossProfileIntentFilterFragment.java │ │ ├── crossprofilewidgetprovider │ │ │ └── ManageCrossProfileWidgetProviderUtil.java │ │ ├── delegation │ │ │ ├── DelegationFragment.java │ │ │ └── DelegationScopesArrayAdapter.java │ │ └── permission │ │ │ ├── AppPermissionsArrayAdapter.java │ │ │ └── ManageAppPermissionsFragment.java │ │ ├── provision │ │ ├── CheckInState.java │ │ ├── GetProvisioningModeActivity.java │ │ ├── PostProvisioningTask.java │ │ ├── ProvisioningSuccessActivity.java │ │ └── ProvisioningUtil.java │ │ ├── search │ │ ├── BaseIndexableFragment.java │ │ ├── IndexableFragments.java │ │ ├── PolicySearchFragment.java │ │ ├── PreferenceCrawler.java │ │ ├── PreferenceIndex.java │ │ ├── PreferenceIndexSqliteOpenHelper.java │ │ ├── PreferenceXmlUtil.java │ │ ├── SearchItemAdapter.java │ │ ├── UserRestrictionIndexableFragment.java │ │ └── XmlIndexableFragment.java │ │ ├── transferownership │ │ └── PickTransferComponentFragment.java │ │ └── util │ │ ├── LooperExecutor.java │ │ ├── MainThreadExecutor.java │ │ └── flags │ │ └── Flags.java └── res │ ├── drawable-hdpi │ ├── ic_launcher.png │ ├── ic_search.png │ └── setup_illustration.png │ ├── drawable-mdpi │ ├── ic_launcher.png │ ├── ic_search.png │ └── setup_illustration.png │ ├── drawable-sw600dp-hdpi │ └── setup_illustration.png │ ├── drawable-sw600dp-mdpi │ └── setup_illustration.png │ ├── drawable-sw600dp-xhdpi │ └── setup_illustration.png │ ├── drawable-sw600dp-xxhdpi │ └── setup_illustration.png │ ├── drawable-sw600dp-xxxhdpi │ └── setup_illustration.png │ ├── drawable-xhdpi │ ├── ic_launcher.png │ ├── ic_search.png │ └── setup_illustration.png │ ├── drawable-xxhdpi │ ├── ic_launcher.png │ ├── ic_search.png │ └── setup_illustration.png │ ├── drawable-xxxhdpi │ └── setup_illustration.png │ ├── drawable │ ├── arrow_down.xml │ ├── arrow_up_down.xml │ ├── ic_circle.xml │ ├── ic_enterprise_blue.xml │ ├── managed_device_setup.xml │ └── managed_profile_setup.xml │ ├── layout │ ├── account_row.xml │ ├── activity_add_account.xml │ ├── activity_get_provisioning_mode.xml │ ├── activity_main.xml │ ├── add_intent_or_intent_filter.xml │ ├── app_row.xml │ ├── available_components_list.xml │ ├── basic_key_value_pair.xml │ ├── certificate_alias_prompt.xml │ ├── certificate_password_prompt.xml │ ├── choose_color_item.xml │ ├── choose_logo_item.xml │ ├── color_picker.xml │ ├── create_and_manage_user_dialog_prompt.xml │ ├── cross_profile_apps.xml │ ├── cross_profile_apps_allowlist.xml │ ├── cross_profile_intent.xml │ ├── data_usage_item.xml │ ├── delegation_scope_row.xml │ ├── eap_tls_wifi_config_dialog.xml │ ├── edit_delete_row.xml │ ├── enable_component_row.xml │ ├── enable_cosu_activity.xml │ ├── esim_dialog_layout.xml │ ├── factory_reset_protection_policy.xml │ ├── factory_reset_protection_policy_account.xml │ ├── finalize_activity.xml │ ├── fragment_media_display.xml │ ├── freeze_period_row.xml │ ├── grant_key_pair_to_app_prompt.xml │ ├── insert_apn.xml │ ├── intent_input.xml │ ├── key_generation_prompt.xml │ ├── key_generation_result.xml │ ├── kiosk_mode_item.xml │ ├── lock_now_dialog_prompt.xml │ ├── lockdown_settings.xml │ ├── manage_apps.xml │ ├── managed_subscriptions_policy.xml │ ├── network_usage_app_history_item.xml │ ├── network_usage_stats.xml │ ├── permission_row.xml │ ├── permitted_input_methods_on_parent.xml │ ├── private_dns_mode.xml │ ├── provisioning_params_display.xml │ ├── proxy_config_dialog.xml │ ├── reset_password_dialog.xml │ ├── reset_password_token.xml │ ├── search_result.xml │ ├── search_result_item.xml │ ├── select_app.xml │ ├── set_permission_policy.xml │ ├── set_support_message.xml │ ├── set_user_session_message.xml │ ├── set_wifi_min_security_level.xml │ ├── set_wifi_ssid_restriction.xml │ ├── setup_management_fragment.xml │ ├── simple_edittext.xml │ ├── string_array_row.xml │ ├── system_update_policy.xml │ ├── transfer_ownership_dialog.xml │ ├── user_row.xml │ ├── wifi_config_dialog.xml │ ├── wifi_config_list_header.xml │ ├── wifi_config_modification.xml │ ├── wipe_data_dialog_prompt.xml │ └── work_policy_info_activity.xml │ ├── menu │ ├── policy_management_menu.xml │ └── policy_search_menu.xml │ ├── raw │ ├── company_disclaimer.txt │ └── emm_disclaimer.txt │ ├── values-sw600dp │ └── dimens.xml │ ├── values-v26 │ └── bools.xml │ ├── values-v34 │ └── bools.xml │ ├── values │ ├── attrs.xml │ ├── bools.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ ├── bind_device_admin_policies.xml │ ├── cross_profile_calendar_preferences.xml │ ├── device_admin_receiver.xml │ ├── device_policy_header.xml │ ├── enterprise_slice_preferences.xml │ ├── esim_control_preferences.xml │ ├── filepaths.xml │ ├── lock_screen_preferences.xml │ ├── lock_task_features_preferences.xml │ ├── override_apn_preferences.xml │ ├── password_constraint_preferences.xml │ └── profile_policy_header.xml ├── replica ├── AndroidManifest.xml └── res │ ├── drawable-hdpi │ └── ic_launcher.png │ ├── drawable-mdpi │ └── ic_launcher.png │ ├── drawable-xhdpi │ └── ic_launcher.png │ ├── drawable-xxhdpi │ └── ic_launcher.png │ └── values │ └── strings.xml └── test └── java └── com └── afwsamples └── testdpc ├── common └── PermissionsHelperTest.java ├── feedback └── AppStatesServiceTest.java ├── policy └── wifimanagement │ └── WifiConfigUtilTest.java ├── provision └── GetProvisioningModeActivityTest.java └── util └── flags ├── BooleanParserTest.java ├── ByteParserTest.java ├── CallbackTest.java ├── CharParserTest.java ├── CustomParserTest.java ├── DoubleParserTest.java ├── FloatParserTest.java ├── HelpTextGenerationTest.java ├── IntParserTest.java ├── InvalidCallsTest.java ├── LongParserTest.java ├── ParamTest.java ├── RegistrationTest.java ├── ShortParserTest.java ├── StringParserTest.java └── Utils.java /.bazelversion: -------------------------------------------------------------------------------- 1 | 7.4.1 2 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - "CONTRIB" 7 | - "LICENSE" 8 | - "PRIVACY" 9 | - "README.md" 10 | pull_request: 11 | branches: [ "master" ] 12 | workflow_dispatch: 13 | 14 | jobs: 15 | build: 16 | name: Build 17 | runs-on: ubuntu-latest 18 | steps: 19 | 20 | - name: Checkout 21 | uses: actions/checkout@v4 22 | 23 | - name: Setup Java 24 | uses: actions/setup-java@v4 25 | with: 26 | java-version: '17' 27 | distribution: 'temurin' 28 | 29 | - name: Setup Bazel 30 | uses: bazel-contrib/setup-bazel@0.8.4 31 | with: 32 | bazelisk-cache: true 33 | disk-cache: ${{ github.workflow }} 34 | repository-cache: true 35 | 36 | - name: Build 37 | run: | 38 | chmod +x build.sh 39 | ./build.sh 40 | 41 | - name: Test 42 | run: bazel test :all 43 | 44 | - name: Set environments 45 | run: | 46 | { 47 | echo "commit=$(echo ${{ github.sha }} | cut -c-7)" 48 | echo "repo=$(echo ${GITHUB_REPOSITORY#$GITHUB_REPOSITORY_OWNER/})" 49 | echo "version=v$(grep -o 'android:versionName="[^"]*"' src/main/AndroidManifest.xml | awk -F'"' '{print $2}')" 50 | } >> $GITHUB_ENV 51 | 52 | - name: Upload APK 53 | uses: actions/upload-artifact@v4 54 | with: 55 | name: ${{ env.repo }}-${{ env.version }}@${{ env.commit }} 56 | path: bazel-bin/*.apk 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bazel-android-testdpc 2 | bazel-bin 3 | bazel-out 4 | bazel-testlogs 5 | **/*.iml 6 | .idea 7 | *.lock 8 | -------------------------------------------------------------------------------- /CONTRIB: -------------------------------------------------------------------------------- 1 | # How to become a contributor and submit your own code 2 | 3 | ## Contributor License Agreements 4 | 5 | We'd love to accept your sample apps and patches! Before we can take them, we 6 | have to jump a couple of legal hurdles. 7 | 8 | Please fill out either the individual or corporate Contributor License Agreement (CLA). 9 | 10 | * If you are an individual writing original source code and you're sure you 11 | own the intellectual property, then you'll need to sign an [individual CLA] 12 | (https://developers.google.com/open-source/cla/individual). 13 | * If you work for a company that wants to allow you to contribute your work, 14 | then you'll need to sign a [corporate CLA] 15 | (https://developers.google.com/open-source/cla/corporate). 16 | 17 | Follow either of the two links above to access the appropriate CLA and 18 | instructions for how to sign and return it. Once we receive it, we'll be able to 19 | accept your pull requests. 20 | 21 | ## Contributing A Patch 22 | 23 | 1. Submit an issue describing your proposed change to the repo in question. 24 | 2. The repo owner will respond to your issue promptly. 25 | 3. If your proposed change is accepted, and you haven't already done so, sign a 26 | Contributor License Agreement (see details above). 27 | 4. Fork the desired repo, develop and test your code changes. 28 | 5. Ensure that your code adheres to the existing style in the sample to which 29 | you are contributing. Refer to the 30 | [Android Code Style Guide] 31 | (https://source.android.com/source/code-style.html) for the 32 | recommended coding standards for this organization. 33 | 6. Ensure that your code has an appropriate set of unit tests which all pass. 34 | 7. Submit a pull request. -------------------------------------------------------------------------------- /MODULE.bazel: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Bazel now uses Bzlmod by default to manage external dependencies. 3 | # Please consider migrating your external dependencies from WORKSPACE to MODULE.bazel. 4 | # 5 | # For more details, please check https://github.com/bazelbuild/bazel/issues/18958 6 | ############################################################################### 7 | -------------------------------------------------------------------------------- /PRIVACY: -------------------------------------------------------------------------------- 1 | This app is for demo purposes only, no data is stored in the app or transferred from it. -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Fail on any error. 4 | set -e 5 | 6 | bazel build testdpc 7 | -------------------------------------------------------------------------------- /qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/android-testdpc/305c86d36e9b7c85e41e9e01fbf2d648fa4970fb/qrcode.png -------------------------------------------------------------------------------- /scripts/ij.bazelproject: -------------------------------------------------------------------------------- 1 | directories: 2 | . 3 | 4 | # Automatically includes all relevant targets under the 'directories' above 5 | derive_targets_from_directories: false 6 | 7 | targets: 8 | testdpc 9 | 10 | additional_languages: 11 | # kotlin 12 | 13 | test_sources: 14 | # Sources that are categorized as test code by the IDE 15 | src/tests/* 16 | 17 | android_sdk_platform: android-34 18 | -------------------------------------------------------------------------------- /setupcompat.BUILD: -------------------------------------------------------------------------------- 1 | android_library( 2 | name = "setupcompat", 3 | srcs = glob([ 4 | "main/java/**/*.java", 5 | "main/java/**/*.kt", 6 | ], exclude = ["partnerconfig/**"]), 7 | custom_package = "com.google.android.setupcompat", 8 | idl_import_root = "external/setupcompat/main/aidl", 9 | idl_parcelables = [ 10 | "main/aidl/com/google/android/setupcompat/portal/NotificationComponent.aidl", 11 | "main/aidl/com/google/android/setupcompat/portal/ProgressServiceComponent.aidl", 12 | ], 13 | idl_srcs = [ 14 | "main/aidl/com/google/android/setupcompat/ISetupCompatService.aidl", 15 | "main/aidl/com/google/android/setupcompat/portal/IPortalProgressCallback.aidl", 16 | "main/aidl/com/google/android/setupcompat/portal/IPortalProgressService.aidl", 17 | "main/aidl/com/google/android/setupcompat/portal/IPortalRegisterResultListener.aidl", 18 | "main/aidl/com/google/android/setupcompat/portal/ISetupNotificationService.aidl", 19 | "main/aidl/com/google/android/setupcompat/portal/v1_1/IPortalProgressCallback.aidl", 20 | ], 21 | manifest = "AndroidManifest.xml", 22 | proguard_specs = ["proguard.flags"], 23 | resource_files = glob([ 24 | "main/res/**", 25 | ]), 26 | deps = [ 27 | ":partnerconfig", 28 | "@maven//:androidx_annotation_annotation", 29 | "@maven//:com_google_errorprone_error_prone_annotations", 30 | ], 31 | visibility = ["//visibility:public"], 32 | ) 33 | 34 | android_library( 35 | name = "partnerconfig", 36 | srcs = glob([ 37 | "partnerconfig/java/**/*.java", 38 | ]), 39 | custom_package = "com.google.android.setupcompat.partnerconfig", 40 | exports_manifest = 1, 41 | manifest = "partnerconfig/AndroidManifest.xml", 42 | deps = [ 43 | ":setupcompat_util", 44 | "@maven//:androidx_annotation_annotation", 45 | "@maven//:androidx_window_window", 46 | ], 47 | visibility = ["//visibility:public"], 48 | ) 49 | 50 | android_library( 51 | name = "setupcompat_util", 52 | srcs = ["main/java/com/google/android/setupcompat/util/BuildCompatUtils.java"], 53 | custom_package = "com.google.android.setupcompat", 54 | manifest = "AndroidManifest.xml", 55 | deps = [ 56 | "@maven//:androidx_annotation_annotation", 57 | ], 58 | ) 59 | -------------------------------------------------------------------------------- /setupdesign.BUILD: -------------------------------------------------------------------------------- 1 | android_library( 2 | name = "setupdesign", 3 | srcs = glob([ 4 | "main/src/**/*.java", 5 | ]), 6 | custom_package = "com.google.android.setupdesign", 7 | exports_manifest = 1, 8 | manifest = "main/AndroidManifest.xml", 9 | proguard_specs = [ 10 | "proguard.flags", 11 | ], 12 | resource_files = glob( 13 | [ 14 | "main/res/**", 15 | ], 16 | exclude_directories = 1, 17 | ), 18 | 19 | deps = [ 20 | ":setupdesign_strings", 21 | "@setupcompat//:setupcompat", 22 | "@setupcompat//:partnerconfig", 23 | "@maven//:com_google_android_material_material", 24 | "@maven//:androidx_annotation_annotation", 25 | "@maven//:androidx_appcompat_appcompat", 26 | "@maven//:androidx_recyclerview_recyclerview", 27 | "@maven//:androidx_window_window", 28 | "@maven//:androidx_core_core", 29 | "@maven//:androidx_customview_customview", 30 | "@maven//:androidx_fragment_fragment", 31 | "@maven//:androidx_vectordrawable_vectordrawable", 32 | "@maven//:com_google_errorprone_error_prone_annotations", 33 | ], 34 | visibility = ["//visibility:public"], 35 | ) 36 | 37 | android_library( 38 | name = "setupdesign_strings", 39 | manifest = "strings/AndroidManifest.xml", 40 | resource_files = glob(["strings/res/**"]), 41 | ) 42 | -------------------------------------------------------------------------------- /src/main/AndroidManifestDebug.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 21 | 22 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/main/aidl/android/content/res/AssetFileDescriptor.aidl: -------------------------------------------------------------------------------- 1 | package android.content.res; 2 | 3 | parcelable AssetFileDescriptor; -------------------------------------------------------------------------------- /src/main/aidl/com/afwsamples/testdpc/comp/IDeviceOwnerService.aidl: -------------------------------------------------------------------------------- 1 | package com.afwsamples.testdpc.comp; 2 | 3 | interface IDeviceOwnerService { 4 | /** 5 | * Notify device owner that work profile is unlocked. 6 | */ 7 | oneway void notifyUserIsUnlocked(in UserHandle callingUserHandle); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/aidl/com/afwsamples/testdpc/comp/IProfileOwnerService.aidl: -------------------------------------------------------------------------------- 1 | package com.afwsamples.testdpc.comp; 2 | 3 | import android.content.res.AssetFileDescriptor; 4 | 5 | import android.os.ParcelFileDescriptor; 6 | 7 | interface IProfileOwnerService { 8 | oneway void setLauncherIconHidden(boolean hidden); 9 | boolean isLauncherIconHidden(); 10 | boolean installCaCertificate(in AssetFileDescriptor afd); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/BootReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc; 18 | 19 | import android.content.BroadcastReceiver; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.os.Process; 23 | import android.os.UserHandle; 24 | import com.afwsamples.testdpc.common.Util; 25 | import com.afwsamples.testdpc.comp.BindDeviceAdminServiceHelper; 26 | import com.afwsamples.testdpc.comp.DeviceOwnerService; 27 | import com.afwsamples.testdpc.comp.IDeviceOwnerService; 28 | 29 | public class BootReceiver extends BroadcastReceiver { 30 | 31 | @Override 32 | public void onReceive(Context context, Intent intent) { 33 | final String action = intent.getAction(); 34 | if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { 35 | if (!Util.isProfileOwner(context) 36 | || Util.getBindDeviceAdminTargetUsers(context).size() == 0) { 37 | return; 38 | } 39 | // We are a profile owner and can bind to the device owner - let's notify the device 40 | // owner that we are up and running (i.e. our user was just started and/or unlocked) 41 | UserHandle targetUser = Util.getBindDeviceAdminTargetUsers(context).get(0); 42 | BindDeviceAdminServiceHelper helper = 43 | createBindDeviceOwnerServiceHelper(context, targetUser); 44 | helper.crossUserCall(service -> service.notifyUserIsUnlocked(Process.myUserHandle())); 45 | } 46 | } 47 | 48 | private BindDeviceAdminServiceHelper createBindDeviceOwnerServiceHelper( 49 | Context context, UserHandle targetUserHandle) { 50 | return new BindDeviceAdminServiceHelper<>( 51 | context, DeviceOwnerService.class, IDeviceOwnerService.Stub::asInterface, targetUserHandle); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/CrossProfileAppsFragment.java: -------------------------------------------------------------------------------- 1 | package com.afwsamples.testdpc; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Fragment; 5 | import android.content.ComponentName; 6 | import android.content.pm.CrossProfileApps; 7 | import android.os.Build.VERSION_CODES; 8 | import android.os.Bundle; 9 | import android.os.UserHandle; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.widget.ImageView; 14 | import android.widget.TextView; 15 | import java.util.List; 16 | 17 | @TargetApi(VERSION_CODES.P) 18 | public class CrossProfileAppsFragment extends Fragment { 19 | private static final String TAG = "CrossProfileAppsFragmen"; 20 | 21 | private View mInflatedView; 22 | private TextView mSwitchProfileTextView; 23 | private TextView mDescriptionTextView; 24 | private ImageView mSwitchProfileImageView; 25 | private CrossProfileApps mCrossProfileApps; 26 | 27 | @Override 28 | public View onCreateView( 29 | LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 30 | mInflatedView = inflater.inflate(R.layout.cross_profile_apps, container, false); 31 | mSwitchProfileTextView = mInflatedView.findViewById(R.id.cross_profile_app_label); 32 | mSwitchProfileImageView = mInflatedView.findViewById(R.id.cross_profile_app_icon); 33 | mDescriptionTextView = mInflatedView.findViewById(R.id.cross_profile_app_description); 34 | return mInflatedView; 35 | } 36 | 37 | @Override 38 | public void onActivityCreated(Bundle savedInstanceState) { 39 | super.onActivityCreated(savedInstanceState); 40 | mCrossProfileApps = getActivity().getSystemService(CrossProfileApps.class); 41 | } 42 | 43 | @Override 44 | public void onResume() { 45 | super.onResume(); 46 | refreshUi(); 47 | } 48 | 49 | private void refreshUi() { 50 | List targetUserProfiles = mCrossProfileApps.getTargetUserProfiles(); 51 | if (targetUserProfiles.isEmpty()) { 52 | showNoTargetUserUi(); 53 | } else { 54 | showHasTargetUserUi(targetUserProfiles.get(0)); 55 | } 56 | } 57 | 58 | private void showNoTargetUserUi() { 59 | mDescriptionTextView.setText(R.string.cross_profile_apps_not_available); 60 | mSwitchProfileTextView.setText(""); 61 | mSwitchProfileImageView.setImageDrawable(null); 62 | mSwitchProfileImageView.setOnClickListener(null); 63 | } 64 | 65 | private void showHasTargetUserUi(UserHandle userHandle) { 66 | mSwitchProfileTextView.setText(mCrossProfileApps.getProfileSwitchingLabel(userHandle)); 67 | mSwitchProfileImageView.setImageDrawable( 68 | mCrossProfileApps.getProfileSwitchingIconDrawable(userHandle)); 69 | mDescriptionTextView.setText(R.string.cross_profile_apps_available); 70 | mSwitchProfileImageView.setOnClickListener( 71 | view -> 72 | mCrossProfileApps.startMainActivity( 73 | new ComponentName(getActivity(), PolicyManagementActivity.class), userHandle)); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/DelegatedAdminReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc; 18 | 19 | import android.annotation.TargetApi; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.net.Uri; 23 | import android.os.Build.VERSION_CODES; 24 | import android.util.Log; 25 | import android.widget.Toast; 26 | 27 | @TargetApi(VERSION_CODES.Q) 28 | public class DelegatedAdminReceiver extends android.app.admin.DelegatedAdminReceiver { 29 | 30 | private static final String TAG = "DelegatedAdminReceiver"; 31 | 32 | @Override 33 | public String onChoosePrivateKeyAlias( 34 | Context context, Intent intent, int uid, Uri uri, String alias) { 35 | return CommonReceiverOperations.onChoosePrivateKeyAlias(context, uid); 36 | } 37 | 38 | @Override 39 | public void onNetworkLogsAvailable( 40 | Context context, Intent intent, long batchToken, int networkLogsCount) { 41 | CommonReceiverOperations.onNetworkLogsAvailable(context, null, batchToken, networkLogsCount); 42 | } 43 | 44 | @Override 45 | public void onSecurityLogsAvailable( 46 | Context context, Intent intent) { 47 | Log.i(TAG, "onSecurityLogsAvailable() called"); 48 | Toast.makeText(context, R.string.on_security_logs_available, Toast.LENGTH_LONG).show(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/DeviceAdminService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afwsamples.testdpc; 17 | 18 | import android.content.BroadcastReceiver; 19 | import android.content.Intent; 20 | import android.content.IntentFilter; 21 | import android.os.Build.VERSION_CODES; 22 | import androidx.annotation.RequiresApi; 23 | import java.io.FileDescriptor; 24 | import java.io.PrintWriter; 25 | 26 | /** 27 | * To allow DPC process to be persistent and foreground. 28 | * 29 | * @see {@link android.app.admin.DeviceAdminService} 30 | */ 31 | @RequiresApi(api = VERSION_CODES.O) 32 | public class DeviceAdminService extends android.app.admin.DeviceAdminService { 33 | 34 | private BroadcastReceiver mPackageChangedReceiver; 35 | 36 | @Override 37 | public void onCreate() { 38 | super.onCreate(); 39 | registerPackageChangesReceiver(); 40 | } 41 | 42 | @Override 43 | public void onDestroy() { 44 | super.onDestroy(); 45 | unregisterPackageChangesReceiver(); 46 | } 47 | 48 | private void registerPackageChangesReceiver() { 49 | IntentFilter intentFilter = new IntentFilter(); 50 | intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 51 | intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 52 | intentFilter.addDataScheme("package"); 53 | mPackageChangedReceiver = new PackageMonitorReceiver(); 54 | getApplicationContext().registerReceiver(mPackageChangedReceiver, intentFilter); 55 | } 56 | 57 | private void unregisterPackageChangesReceiver() { 58 | if (mPackageChangedReceiver != null) { 59 | getApplicationContext().unregisterReceiver(mPackageChangedReceiver); 60 | mPackageChangedReceiver = null; 61 | } 62 | } 63 | 64 | @Override 65 | protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 66 | new ShellCommand(getApplicationContext(), writer, args).run(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/PackageMonitorReceiver.java: -------------------------------------------------------------------------------- 1 | package com.afwsamples.testdpc; 2 | 3 | import android.app.Notification; 4 | import android.app.NotificationManager; 5 | import android.content.BroadcastReceiver; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.text.TextUtils; 9 | import androidx.core.app.NotificationCompat; 10 | import com.afwsamples.testdpc.common.NotificationUtil; 11 | 12 | public class PackageMonitorReceiver extends BroadcastReceiver { 13 | private static final String TAG = "PackageMonitorReceiver"; 14 | private static final int PACKAGE_CHANGED_NOTIIFICATION_ID = 34857; 15 | 16 | @Override 17 | public void onReceive(Context context, Intent intent) { 18 | String action = intent.getAction(); 19 | if (!Intent.ACTION_PACKAGE_ADDED.equals(action) 20 | && !Intent.ACTION_PACKAGE_REMOVED.equals(action)) { 21 | return; 22 | } 23 | String packageName = getPackageNameFromIntent(intent); 24 | if (TextUtils.isEmpty(packageName)) { 25 | return; 26 | } 27 | boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); 28 | if (replacing) { 29 | return; 30 | } 31 | String notificationBody = buildNotificationText(context, packageName, action); 32 | Notification notification = 33 | NotificationUtil.getNotificationBuilder(context) 34 | .setSmallIcon(R.drawable.ic_launcher) 35 | .setContentTitle(context.getString(R.string.package_changed_notification_title)) 36 | .setContentText(notificationBody) 37 | .setStyle(new NotificationCompat.BigTextStyle().bigText(notificationBody)) 38 | .setDefaults(Notification.DEFAULT_LIGHTS) 39 | .setOnlyAlertOnce(true) 40 | .build(); 41 | NotificationManager notificationManager = 42 | (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 43 | notificationManager.notify(PACKAGE_CHANGED_NOTIIFICATION_ID, notification); 44 | } 45 | 46 | private String getPackageNameFromIntent(Intent intent) { 47 | if (intent.getData() == null) { 48 | return null; 49 | } 50 | return intent.getData().getSchemeSpecificPart(); 51 | } 52 | 53 | private String buildNotificationText(Context context, String pkgName, String action) { 54 | int res = 55 | Intent.ACTION_PACKAGE_ADDED.equals(action) 56 | ? R.string.package_added_notification_text 57 | : R.string.package_removed_notification_text; 58 | return context.getString(res, pkgName); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/SetupManagementActivity.java: -------------------------------------------------------------------------------- 1 | package com.afwsamples.testdpc; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import com.afwsamples.testdpc.common.ThemeUtil; 6 | import com.google.android.setupcompat.util.WizardManagerHelper; 7 | 8 | public class SetupManagementActivity extends Activity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | 14 | // get default theme string from suw intent extra and set the Theme. 15 | ThemeUtil.setTheme(this, getIntent().getStringExtra(WizardManagerHelper.EXTRA_THEME)); 16 | 17 | setContentView(R.layout.activity_main); 18 | if (savedInstanceState == null) { 19 | getFragmentManager() 20 | .beginTransaction() 21 | .add(R.id.container, new SetupManagementFragment(), SetupManagementFragment.FRAGMENT_TAG) 22 | .commit(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/WorkPolicyInfoActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afwsamples.testdpc; 17 | 18 | import android.app.Activity; 19 | import android.os.Bundle; 20 | 21 | public final class WorkPolicyInfoActivity extends Activity { 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.work_policy_info_activity); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/common/AccountArrayAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc.common; 18 | 19 | import android.accounts.Account; 20 | import android.accounts.AccountManager; 21 | import android.accounts.AuthenticatorDescription; 22 | import android.content.Context; 23 | import android.content.pm.PackageManager; 24 | import android.view.LayoutInflater; 25 | import android.view.View; 26 | import android.view.ViewGroup; 27 | import android.widget.ArrayAdapter; 28 | import android.widget.ImageView; 29 | import android.widget.TextView; 30 | import com.afwsamples.testdpc.R; 31 | import java.util.HashMap; 32 | import java.util.List; 33 | import java.util.Map; 34 | 35 | /** A simple adapter which takes a list of accounts in a listview. */ 36 | public class AccountArrayAdapter extends ArrayAdapter { 37 | private static final String TAG = "AccountArrayAdapter"; 38 | 39 | private PackageManager mPackageManager; 40 | private Map mAuthenticatorMap; 41 | 42 | public AccountArrayAdapter(Context context, int resource, List accountList) { 43 | super(context, resource, accountList); 44 | mPackageManager = context.getPackageManager(); 45 | mAuthenticatorMap = new HashMap<>(); 46 | AccountManager accountManager = AccountManager.get(context); 47 | for (AuthenticatorDescription authenticator : accountManager.getAuthenticatorTypes()) { 48 | mAuthenticatorMap.put(authenticator.type, authenticator); 49 | } 50 | } 51 | 52 | @Override 53 | public View getView(int position, View convertView, ViewGroup parent) { 54 | if (convertView == null) { 55 | convertView = LayoutInflater.from(getContext()).inflate(R.layout.account_row, parent, false); 56 | } 57 | 58 | Account account = getItem(position); 59 | AuthenticatorDescription authenticator = mAuthenticatorMap.get(account.type); 60 | final ImageView iconImageView = convertView.findViewById(R.id.account_type_icon); 61 | final TextView accountNameTextView = convertView.findViewById(R.id.account_name); 62 | iconImageView.setImageDrawable( 63 | mPackageManager.getDrawable(authenticator.packageName, authenticator.iconId, null)); 64 | accountNameTextView.setText(account.name); 65 | return convertView; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/common/AppInfoArrayAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc.common; 18 | 19 | import android.content.Context; 20 | import android.content.pm.ApplicationInfo; 21 | import android.content.pm.PackageManager; 22 | import android.view.LayoutInflater; 23 | import android.view.View; 24 | import android.view.ViewGroup; 25 | import android.widget.ArrayAdapter; 26 | import android.widget.ImageView; 27 | import android.widget.TextView; 28 | import com.afwsamples.testdpc.R; 29 | import java.util.List; 30 | 31 | /** 32 | * A simple adapter which takes a list of package names and shows their app icon and app name in a 33 | * listview. 34 | */ 35 | public class AppInfoArrayAdapter extends ArrayAdapter { 36 | private PackageManager mPackageManager; 37 | private int mAppInfoFlags = 0; 38 | private static final String TAG = "AppInfoArrayAdapter"; 39 | 40 | public AppInfoArrayAdapter( 41 | Context context, int resource, List pkgNameList, boolean includeDisabledApps) { 42 | super(context, resource, pkgNameList); 43 | mPackageManager = getContext().getPackageManager(); 44 | if (includeDisabledApps) { 45 | mAppInfoFlags = PackageManager.GET_UNINSTALLED_PACKAGES; 46 | } 47 | } 48 | 49 | public AppInfoArrayAdapter(Context context, int resource, List pkgNameList) { 50 | this(context, resource, pkgNameList, false /* Don't include disabled apps */); 51 | } 52 | 53 | @Override 54 | public View getView(int position, View convertView, ViewGroup parent) { 55 | if (convertView == null) { 56 | convertView = LayoutInflater.from(getContext()).inflate(R.layout.app_row, parent, false); 57 | } 58 | 59 | final ImageView iconImageView = (ImageView) convertView.findViewById(R.id.pkg_icon); 60 | final TextView pkgNameTextView = (TextView) convertView.findViewById(R.id.pkg_name); 61 | try { 62 | ApplicationInfo applicationInfo = 63 | mPackageManager.getApplicationInfo(getItem(position), mAppInfoFlags); 64 | iconImageView.setImageDrawable(mPackageManager.getApplicationIcon(applicationInfo)); 65 | pkgNameTextView.setText(mPackageManager.getApplicationLabel(applicationInfo)); 66 | } catch (PackageManager.NameNotFoundException e) { 67 | // The package has probably been uninstalled so just show it's package name 68 | iconImageView.setImageDrawable(null); 69 | pkgNameTextView.setText(getItem(position)); 70 | } 71 | return convertView; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/common/AppInfoSpinnerAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc.common; 18 | 19 | import android.content.Context; 20 | import android.content.pm.ApplicationInfo; 21 | import android.content.pm.PackageManager; 22 | import android.view.LayoutInflater; 23 | import android.view.View; 24 | import android.view.ViewGroup; 25 | import android.widget.ArrayAdapter; 26 | import android.widget.ImageView; 27 | import android.widget.SpinnerAdapter; 28 | import android.widget.TextView; 29 | import com.afwsamples.testdpc.R; 30 | import java.util.List; 31 | 32 | /** An array adapter which shows an application name and its icon in a spinner view. */ 33 | public class AppInfoSpinnerAdapter extends ArrayAdapter implements SpinnerAdapter { 34 | 35 | private PackageManager mPackageManager; 36 | 37 | public AppInfoSpinnerAdapter( 38 | Context context, int resource, int textViewResourceId, List objects) { 39 | super(context, resource, textViewResourceId, objects); 40 | mPackageManager = context.getPackageManager(); 41 | } 42 | 43 | @Override 44 | public View getDropDownView(int position, View convertView, ViewGroup parent) { 45 | return getCustomView(position, convertView, parent); 46 | } 47 | 48 | @Override 49 | public View getView(int position, View convertView, ViewGroup parent) { 50 | return getCustomView(position, convertView, parent); 51 | } 52 | 53 | private View getCustomView(int position, View convertView, ViewGroup parent) { 54 | if (convertView == null) { 55 | convertView = LayoutInflater.from(getContext()).inflate(R.layout.app_row, parent, false); 56 | } 57 | ApplicationInfo applicationInfo = getItem(position); 58 | ImageView iconImageView = (ImageView) convertView.findViewById(R.id.pkg_icon); 59 | iconImageView.setImageDrawable(mPackageManager.getApplicationIcon(applicationInfo)); 60 | TextView pkgNameTextView = (TextView) convertView.findViewById(R.id.pkg_name); 61 | pkgNameTextView.setText(mPackageManager.getApplicationLabel(applicationInfo)); 62 | return convertView; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/common/BundleUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc.common; 18 | 19 | import android.annotation.TargetApi; 20 | import android.os.Build.VERSION_CODES; 21 | import android.os.Bundle; 22 | import android.os.PersistableBundle; 23 | import java.util.Set; 24 | 25 | public class BundleUtil { 26 | 27 | @TargetApi(VERSION_CODES.LOLLIPOP_MR1) 28 | public static PersistableBundle bundleToPersistableBundle(Bundle bundle) { 29 | Set keySet = bundle.keySet(); 30 | PersistableBundle persistableBundle = new PersistableBundle(); 31 | for (String key : keySet) { 32 | Object value = bundle.get(key); 33 | if (value instanceof Boolean) { 34 | persistableBundle.putBoolean(key, (boolean) value); 35 | } else if (value instanceof Integer) { 36 | persistableBundle.putInt(key, (int) value); 37 | } else if (value instanceof String) { 38 | persistableBundle.putString(key, (String) value); 39 | } else if (value instanceof String[]) { 40 | persistableBundle.putStringArray(key, (String[]) value); 41 | } else if (value instanceof Bundle) { 42 | PersistableBundle innerBundle = bundleToPersistableBundle((Bundle) value); 43 | persistableBundle.putPersistableBundle(key, innerBundle); 44 | } 45 | } 46 | return persistableBundle; 47 | } 48 | 49 | @TargetApi(VERSION_CODES.LOLLIPOP_MR1) 50 | public static Bundle persistableBundleToBundle(PersistableBundle persistableBundle) { 51 | Set keySet = persistableBundle.keySet(); 52 | Bundle bundle = new Bundle(); 53 | for (String key : keySet) { 54 | Object value = persistableBundle.get(key); 55 | if (value instanceof Boolean) { 56 | bundle.putBoolean(key, (boolean) value); 57 | } else if (value instanceof Integer) { 58 | bundle.putInt(key, (int) value); 59 | } else if (value instanceof String) { 60 | bundle.putString(key, (String) value); 61 | } else if (value instanceof String[]) { 62 | bundle.putStringArray(key, (String[]) value); 63 | } else if (value instanceof PersistableBundle) { 64 | Bundle innerBundle = persistableBundleToBundle((PersistableBundle) value); 65 | bundle.putBundle(key, innerBundle); 66 | } 67 | } 68 | return bundle; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/common/Dumpable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afwsamples.testdpc.common; 17 | 18 | import java.io.FileDescriptor; 19 | import java.io.PrintWriter; 20 | 21 | /** Base class for all components that implements a custom {@code dump()} method. */ 22 | public interface Dumpable { 23 | 24 | /** 25 | * Checks whether the arguments contains the option to just dump this app's state (and skipping 26 | * Android SDK state). 27 | */ 28 | public static boolean isQuietMode(String[] args) { 29 | return args != null && args.length > 0 && (args[0].equals("-q") || args[0].equals("--quiet")); 30 | } 31 | 32 | /** 33 | * Custom dump that should only dump this app's state (and skip Android SDK state) when {@code 34 | * quietModeOnly} is {@code true}. 35 | */ 36 | void dump(String prefix, PrintWriter pw, FileDescriptor fd, boolean quietModeOnly, String[] args); 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/common/DumpableActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afwsamples.testdpc.common; 17 | 18 | import android.app.Activity; 19 | import android.app.Fragment; 20 | import java.io.FileDescriptor; 21 | import java.io.PrintWriter; 22 | import java.util.List; 23 | 24 | /** Base class for all activities that implements {@code dump()}. */ 25 | public abstract class DumpableActivity extends Activity { 26 | 27 | @Override 28 | public void dump(String prefix, FileDescriptor fd, PrintWriter pw, String[] args) { 29 | boolean quietMode = Dumpable.isQuietMode(args); 30 | if (quietMode) { 31 | List fragments = getFragmentManager().getFragments(); 32 | pw.println("*** Dumping Dumpable fragments only ***"); 33 | String prefix2 = prefix + prefix; 34 | for (Fragment fragment : fragments) { 35 | if (fragment instanceof Dumpable) { 36 | pw.printf("%s%s:\n", prefix, fragment); 37 | ((Dumpable) fragment).dump(prefix2, pw, fd, quietMode, args); 38 | } 39 | } 40 | return; 41 | } 42 | 43 | super.dump(prefix, fd, pw, args); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/common/ManageAppFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc.common; 18 | 19 | import android.content.pm.ApplicationInfo; 20 | import android.widget.SpinnerAdapter; 21 | import com.afwsamples.testdpc.R; 22 | import java.util.ArrayList; 23 | import java.util.Collections; 24 | import java.util.HashSet; 25 | import java.util.List; 26 | import java.util.Set; 27 | 28 | public abstract class ManageAppFragment extends BaseManageComponentFragment { 29 | /** List of packages always shown in the app list. */ 30 | private static final Set ALLOWLISTED_APPS = new HashSet<>(); 31 | 32 | static { 33 | // GmsCore 34 | ALLOWLISTED_APPS.add("com.google.android.gms"); 35 | } 36 | 37 | @Override 38 | protected SpinnerAdapter createSpinnerAdapter() { 39 | List managedAppList = getInstalledOrLaunchableApps(); 40 | Collections.sort(managedAppList, new ApplicationInfo.DisplayNameComparator(mPackageManager)); 41 | return new AppInfoSpinnerAdapter( 42 | getActivity(), R.layout.app_row, R.id.pkg_name, managedAppList); 43 | } 44 | 45 | /** 46 | * Additionally filter apps returned in the list, return {@code true} to keep the app in the list, 47 | * {@code false} to exclude it. 48 | */ 49 | protected boolean filterApp(ApplicationInfo info) { 50 | return true; 51 | } 52 | 53 | private List getInstalledOrLaunchableApps() { 54 | List installedApps = 55 | mPackageManager.getInstalledApplications(0 /* Default flags */); 56 | List filteredAppList = new ArrayList<>(); 57 | for (ApplicationInfo applicationInfo : installedApps) { 58 | if (mPackageManager.getLaunchIntentForPackage(applicationInfo.packageName) != null 59 | || (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0 60 | || ALLOWLISTED_APPS.contains(applicationInfo.packageName)) { 61 | if (filterApp(applicationInfo)) { 62 | filteredAppList.add(applicationInfo); 63 | } 64 | } 65 | } 66 | return filteredAppList; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/common/ManageResolveInfoFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc.common; 18 | 19 | import android.content.pm.ResolveInfo; 20 | import android.os.Bundle; 21 | import android.widget.SpinnerAdapter; 22 | import com.afwsamples.testdpc.R; 23 | import java.util.List; 24 | 25 | /** 26 | * This fragment shows a spinner of all allowed apps and a list of properties associated with the 27 | * currently selected application. 28 | */ 29 | public abstract class ManageResolveInfoFragment extends BaseManageComponentFragment { 30 | 31 | protected List mResolveInfos; 32 | 33 | @Override 34 | public void onCreate(Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | } 37 | 38 | @Override 39 | protected SpinnerAdapter createSpinnerAdapter() { 40 | mResolveInfos = loadResolveInfoList(); 41 | return new ResolveInfoSpinnerAdapter( 42 | getActivity(), R.layout.app_row, R.id.pkg_name, mResolveInfos); 43 | } 44 | 45 | protected abstract List loadResolveInfoList(); 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/common/NotificationUtil.java: -------------------------------------------------------------------------------- 1 | package com.afwsamples.testdpc.common; 2 | 3 | import android.app.Notification; 4 | import android.app.NotificationChannel; 5 | import android.app.NotificationManager; 6 | import android.content.Context; 7 | import android.os.Build.VERSION_CODES; 8 | import androidx.annotation.RequiresApi; 9 | import androidx.annotation.StringRes; 10 | import androidx.core.app.NotificationCompat; 11 | import com.afwsamples.testdpc.R; 12 | 13 | public class NotificationUtil { 14 | private static final String TAG = "NotificationUtil"; 15 | private static final String DEFAULT_CHANNEL_ID = "default_testdpc_channel"; 16 | public static final int BUGREPORT_NOTIFICATION_ID = 1; 17 | public static final int PASSWORD_EXPIRATION_NOTIFICATION_ID = 2; 18 | public static final int USER_ADDED_NOTIFICATION_ID = 3; 19 | public static final int USER_REMOVED_NOTIFICATION_ID = 4; 20 | public static final int USER_STARTED_NOTIFICATION_ID = 5; 21 | public static final int USER_STOPPED_NOTIFICATION_ID = 6; 22 | public static final int USER_SWITCHED_NOTIFICATION_ID = 7; 23 | public static final int PROFILE_OWNER_CHANGED_ID = 8; 24 | public static final int DEVICE_OWNER_CHANGED_ID = 9; 25 | public static final int TRANSFER_OWNERSHIP_COMPLETE_ID = 10; 26 | public static final int TRANSFER_AFFILIATED_PROFILE_OWNERSHIP_COMPLETE_ID = 11; 27 | 28 | public static void showNotification( 29 | Context context, @StringRes int titleId, String msg, int notificationId) { 30 | NotificationManager notificationManager = 31 | (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 32 | Notification notification = 33 | getNotificationBuilder(context) 34 | .setSmallIcon(R.drawable.ic_launcher) 35 | .setContentTitle(context.getString(titleId)) 36 | .setContentText(msg) 37 | .setStyle(new NotificationCompat.BigTextStyle().bigText(msg)) 38 | .build(); 39 | notificationManager.notify(notificationId, notification); 40 | } 41 | 42 | public static NotificationCompat.Builder getNotificationBuilder(Context context) { 43 | if (Util.SDK_INT >= VERSION_CODES.O) { 44 | createDefaultNotificationChannel(context); 45 | } 46 | NotificationCompat.Builder builder = 47 | new NotificationCompat.Builder(context, DEFAULT_CHANNEL_ID); 48 | return builder; 49 | } 50 | 51 | @RequiresApi(VERSION_CODES.O) 52 | private static void createDefaultNotificationChannel(Context context) { 53 | NotificationManager notificationManager = 54 | (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 55 | String appName = context.getString(R.string.app_name); 56 | NotificationChannel channel = 57 | new NotificationChannel( 58 | DEFAULT_CHANNEL_ID, appName, NotificationManager.IMPORTANCE_DEFAULT); 59 | channel.setImportance(NotificationManager.IMPORTANCE_LOW); 60 | notificationManager.createNotificationChannel(channel); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/common/OnBackPressedHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.afwsamples.testdpc.common; 17 | 18 | /** Handler for implementing onBackPressed() functionality in Fragments. */ 19 | public interface OnBackPressedHandler { 20 | 21 | /* 22 | * Returns true if the subclass has handled the back button press, otherwise the caller should 23 | * call Activity.onBackPressed(), so the default back button behaviour is triggered. 24 | */ 25 | public boolean onBackPressed(); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/common/ResolveInfoSpinnerAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under theM License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc.common; 18 | 19 | import android.content.Context; 20 | import android.content.pm.PackageManager; 21 | import android.content.pm.ResolveInfo; 22 | import android.view.LayoutInflater; 23 | import android.view.View; 24 | import android.view.ViewGroup; 25 | import android.widget.ArrayAdapter; 26 | import android.widget.ImageView; 27 | import android.widget.SpinnerAdapter; 28 | import android.widget.TextView; 29 | import com.afwsamples.testdpc.R; 30 | import java.util.List; 31 | 32 | /** An array adapter which shows an application name and its icon in a spinner view. */ 33 | public class ResolveInfoSpinnerAdapter extends ArrayAdapter implements SpinnerAdapter { 34 | 35 | private PackageManager mPackageManager; 36 | 37 | public ResolveInfoSpinnerAdapter( 38 | Context context, int resource, int textViewResourceId, List objects) { 39 | super(context, resource, textViewResourceId, objects); 40 | mPackageManager = context.getPackageManager(); 41 | } 42 | 43 | @Override 44 | public View getDropDownView(int position, View convertView, ViewGroup parent) { 45 | return getCustomView(position, convertView, parent); 46 | } 47 | 48 | @Override 49 | public View getView(int position, View convertView, ViewGroup parent) { 50 | return getCustomView(position, convertView, parent); 51 | } 52 | 53 | private View getCustomView(int position, View convertView, ViewGroup parent) { 54 | if (convertView == null) { 55 | convertView = LayoutInflater.from(getContext()).inflate(R.layout.app_row, parent, false); 56 | } 57 | ResolveInfo resolveInfo = getItem(position); 58 | ImageView iconImageView = (ImageView) convertView.findViewById(R.id.pkg_icon); 59 | iconImageView.setImageDrawable(resolveInfo.loadIcon(mPackageManager)); 60 | TextView pkgNameTextView = (TextView) convertView.findViewById(R.id.pkg_name); 61 | pkgNameTextView.setText(resolveInfo.loadLabel(mPackageManager)); 62 | return convertView; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/common/ThemeUtil.java: -------------------------------------------------------------------------------- 1 | package com.afwsamples.testdpc.common; 2 | 3 | import android.content.Context; 4 | import android.os.Build.VERSION_CODES; 5 | import com.afwsamples.testdpc.R; 6 | import com.google.android.setupdesign.util.ThemeHelper; 7 | import com.google.android.setupdesign.util.ThemeResolver; 8 | 9 | /** Common utility functions for Theming. */ 10 | public final class ThemeUtil { 11 | 12 | /** 13 | * Set the correct Theme for the SUW screen 14 | * 15 | * @param default theme string . 16 | */ 17 | public static void setTheme(Context context, String themeName) { 18 | int defaultTheme; 19 | if (Util.SDK_INT < VERSION_CODES.TIRAMISU) { 20 | defaultTheme = 21 | ThemeHelper.isSetupWizardDayNightEnabled(context) 22 | ? R.style.SudThemeGlifV3_DayNight 23 | : R.style.SudThemeGlifV3_Light; 24 | } else { 25 | defaultTheme = 26 | ThemeHelper.isSetupWizardDayNightEnabled(context) 27 | ? R.style.SudThemeGlifV4_DayNight 28 | : R.style.SudThemeGlifV4_Light; 29 | } 30 | 31 | // a. set GlifTheme based on suw intent extra & SUW daynight flag. 32 | ThemeResolver THEME_RESOLVER = 33 | new ThemeResolver.Builder(ThemeResolver.getDefault()) 34 | .setDefaultTheme(defaultTheme) 35 | .setUseDayNight(true) 36 | .build(); 37 | 38 | // If outside suw (themeName=null); the themeResolver will fallback 39 | // to the default theme directly, resolve theme resource based on the day-night flag. 40 | int themeResId = 41 | THEME_RESOLVER.resolve(themeName, !ThemeHelper.isSetupWizardDayNightEnabled(context)); 42 | 43 | // setTheme for this activity. 44 | context.setTheme(themeResId); 45 | 46 | // b. overlay color attrs to dynamic color for GlifTheme. 47 | ThemeHelper.trySetDynamicColor(context); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/common/UserArrayAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc.common; 18 | 19 | import android.annotation.TargetApi; 20 | import android.content.Context; 21 | import android.content.res.Resources; 22 | import android.os.Build.VERSION_CODES; 23 | import android.os.UserHandle; 24 | import android.os.UserManager; 25 | import android.view.LayoutInflater; 26 | import android.view.View; 27 | import android.view.ViewGroup; 28 | import android.widget.ArrayAdapter; 29 | import android.widget.TextView; 30 | import com.afwsamples.testdpc.R; 31 | import java.util.List; 32 | 33 | /** A simple adapter which takes a list of user serial numbers in a listview. */ 34 | @TargetApi(VERSION_CODES.M) 35 | public class UserArrayAdapter extends ArrayAdapter { 36 | private static final String TAG = "UserArrayAdapter"; 37 | 38 | private UserManager mUserManager; 39 | private Resources mResources; 40 | 41 | public UserArrayAdapter(Context context, int resource, List userHandleList) { 42 | super(context, resource, userHandleList); 43 | mUserManager = context.getSystemService(UserManager.class); 44 | mResources = context.getResources(); 45 | } 46 | 47 | @Override 48 | public View getView(int position, View convertView, ViewGroup parent) { 49 | if (convertView == null) { 50 | convertView = LayoutInflater.from(getContext()).inflate(R.layout.user_row, parent, false); 51 | } 52 | 53 | final TextView userNameTextView = convertView.findViewById(R.id.user_name); 54 | userNameTextView.setText( 55 | mResources.getString( 56 | R.string.user_string, mUserManager.getSerialNumberForUser(getItem(position)))); 57 | return convertView; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/common/preference/CustomConstraint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc.common.preference; 18 | 19 | import androidx.annotation.StringRes; 20 | 21 | public interface CustomConstraint { 22 | 23 | /** 24 | * Return the string resource int of constraint summary if a custom constraint is not met, {@code 25 | * 0} otherwise. 26 | */ 27 | @StringRes 28 | int validateConstraint(); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/common/preference/DpcPreferenceBase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc.common.preference; 18 | 19 | import androidx.annotation.Nullable; 20 | 21 | /** Common base class for the DpcPreference family of classes. */ 22 | public interface DpcPreferenceBase { 23 | void setMinSdkVersion(int version); 24 | 25 | void setAdminConstraint(@DpcPreferenceHelper.AdminKind int adminConstraint); 26 | 27 | void clearAdminConstraint(); 28 | 29 | void setUserConstraint(@DpcPreferenceHelper.UserKind int userConstraints); 30 | 31 | void clearUserConstraint(); 32 | 33 | void setPermissionConstraint(String permissionConstraints); 34 | 35 | void clearPermissionConstraint(); 36 | 37 | void clearNonCustomConstraints(); 38 | 39 | void setCustomConstraint(@Nullable CustomConstraint customConstraint); 40 | 41 | void addCustomConstraint(@Nullable CustomConstraint customConstraint); 42 | /** To re-check is the constraint met and enable/disable the preference accordingly. */ 43 | void refreshEnabledState(); 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/comp/DeviceOwnerService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc.comp; 18 | 19 | import android.app.Service; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.os.Binder; 23 | import android.os.IBinder; 24 | import android.os.RemoteException; 25 | import android.os.UserHandle; 26 | import android.os.UserManager; 27 | import android.util.Log; 28 | import com.afwsamples.testdpc.R; 29 | import com.afwsamples.testdpc.common.NotificationUtil; 30 | 31 | public class DeviceOwnerService extends Service { 32 | private static final String TAG = "DeviceOwnerService"; 33 | 34 | private Binder mBinder; 35 | 36 | @Override 37 | public void onCreate() { 38 | mBinder = new DeviceOwnerServiceImpl(this); 39 | } 40 | 41 | @Override 42 | public IBinder onBind(Intent intent) { 43 | return mBinder; 44 | } 45 | 46 | static class DeviceOwnerServiceImpl extends IDeviceOwnerService.Stub { 47 | private final Context mContext; 48 | private final UserManager mUserManager; 49 | 50 | private DeviceOwnerServiceImpl(Context context) { 51 | mContext = context; 52 | mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 53 | } 54 | 55 | @Override 56 | public void notifyUserIsUnlocked(UserHandle callingUserHandle) throws RemoteException { 57 | long userSerialNumber = mUserManager.getSerialNumberForUser(callingUserHandle); 58 | NotificationUtil.showNotification( 59 | mContext, 60 | R.string.po_user_status, 61 | mContext.getString(R.string.po_user_is_unlocked, userSerialNumber), 62 | 0); 63 | Log.d(TAG, "notifyUserIsUnlocked() called for user with serial " + userSerialNumber); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/comp/OnServiceConnectedListener.java: -------------------------------------------------------------------------------- 1 | package com.afwsamples.testdpc.comp; 2 | 3 | import android.os.RemoteException; 4 | import androidx.annotation.UiThread; 5 | 6 | public interface OnServiceConnectedListener { 7 | @UiThread 8 | void onServiceConnected(T service) throws RemoteException; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/comp/ServiceInterfaceConverter.java: -------------------------------------------------------------------------------- 1 | package com.afwsamples.testdpc.comp; 2 | 3 | import android.os.IBinder; 4 | 5 | public interface ServiceInterfaceConverter { 6 | T convert(IBinder iBinder); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/cosu/CosuUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc.cosu; 18 | 19 | import android.annotation.SuppressLint; 20 | import android.app.DownloadManager; 21 | import android.net.Uri; 22 | import android.os.Handler; 23 | import android.util.Log; 24 | 25 | /** Utility class for various operations necessary during COSU set up. */ 26 | /* package */ class CosuUtils { 27 | public static final String TAG = "CosuSetup"; 28 | public static final boolean DEBUG = false; 29 | 30 | public static final int MSG_DOWNLOAD_COMPLETE = 1; 31 | public static final int MSG_DOWNLOAD_TIMEOUT = 2; 32 | public static final int MSG_INSTALL_COMPLETE = 3; 33 | 34 | private static final int DOWNLOAD_TIMEOUT_MILLIS = 120_000; 35 | 36 | @SuppressLint("DownloadManager") 37 | public static Long startDownload(DownloadManager dm, Handler handler, String location) { 38 | DownloadManager.Request request = new DownloadManager.Request(Uri.parse(location)); 39 | Long id = dm.enqueue(request); 40 | handler.sendMessageDelayed( 41 | handler.obtainMessage(MSG_DOWNLOAD_TIMEOUT, id), DOWNLOAD_TIMEOUT_MILLIS); 42 | if (DEBUG) Log.d(TAG, "Starting download: DownloadId=" + id); 43 | return id; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/policy/ManageAffiliationIdsFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc.policy; 18 | 19 | import android.annotation.TargetApi; 20 | import android.os.Build.VERSION_CODES; 21 | import android.os.Bundle; 22 | import androidx.collection.ArraySet; 23 | import com.afwsamples.testdpc.DevicePolicyManagerGateway; 24 | import com.afwsamples.testdpc.DevicePolicyManagerGatewayImpl; 25 | import com.afwsamples.testdpc.R; 26 | import java.util.Collection; 27 | import java.util.List; 28 | import java.util.Set; 29 | 30 | /** 31 | * Allows the user to see / edit / delete affiliation ids. See {@link 32 | * DevicePolicyManager#setAffiliationIds(ComponentName, Set)} 33 | */ 34 | public class ManageAffiliationIdsFragment extends BaseStringItemsFragment { 35 | 36 | private DevicePolicyManagerGateway mDevicePolicyManagerGateway; 37 | 38 | public ManageAffiliationIdsFragment() { 39 | super( 40 | R.string.manage_affiliation_ids, 41 | R.string.enter_affiliation_id, 42 | R.string.affiliation_id_empty_error); 43 | } 44 | 45 | @Override 46 | public void onCreate(Bundle savedInstanceState) { 47 | super.onCreate(savedInstanceState); 48 | 49 | mDevicePolicyManagerGateway = new DevicePolicyManagerGatewayImpl(getActivity()); 50 | } 51 | 52 | @TargetApi(VERSION_CODES.O) 53 | @Override 54 | protected Collection loadItems() { 55 | return mDevicePolicyManagerGateway.getAffiliationIds(); 56 | } 57 | 58 | @TargetApi(VERSION_CODES.O) 59 | @Override 60 | protected void saveItems(List items) { 61 | mDevicePolicyManagerGateway.setAffiliationIds(new ArraySet<>(items)); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/policy/ManageKeepUninstalledPackagesFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc.policy; 18 | 19 | import android.annotation.TargetApi; 20 | import android.app.admin.DevicePolicyManager; 21 | import android.content.ComponentName; 22 | import android.content.Context; 23 | import android.os.Build.VERSION_CODES; 24 | import android.os.Bundle; 25 | import com.afwsamples.testdpc.DeviceAdminReceiver; 26 | import com.afwsamples.testdpc.R; 27 | import java.util.Collection; 28 | import java.util.Collections; 29 | import java.util.List; 30 | 31 | /** 32 | * Allows the user to see / edit / delete keep uninstalled packages. See {@link 33 | * DevicePolicyManager#setKeepUninstalledPackages(ComponentName, List)} 34 | */ 35 | public class ManageKeepUninstalledPackagesFragment extends BaseStringItemsFragment { 36 | 37 | private DevicePolicyManager mDevicePolicyManager; 38 | private ComponentName mAdminComponent; 39 | 40 | public ManageKeepUninstalledPackagesFragment() { 41 | super( 42 | R.string.keep_uninstalled_packages, 43 | R.string.enter_package_name, 44 | R.string.package_name_empty_error); 45 | } 46 | 47 | @Override 48 | public void onCreate(Bundle savedInstanceState) { 49 | super.onCreate(savedInstanceState); 50 | 51 | mDevicePolicyManager = 52 | (DevicePolicyManager) getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE); 53 | mAdminComponent = DeviceAdminReceiver.getComponentName(getActivity()); 54 | } 55 | 56 | @TargetApi(VERSION_CODES.P) 57 | @Override 58 | protected Collection loadItems() { 59 | List packages = mDevicePolicyManager.getKeepUninstalledPackages(mAdminComponent); 60 | return packages == null ? Collections.emptyList() : packages; 61 | } 62 | 63 | @TargetApi(VERSION_CODES.P) 64 | @Override 65 | protected void saveItems(List items) { 66 | mDevicePolicyManager.setKeepUninstalledPackages(mAdminComponent, items); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/policy/certificate/DelegatedCertInstallerFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc.policy.certificate; 18 | 19 | import android.annotation.TargetApi; 20 | import android.app.admin.DevicePolicyManager; 21 | import android.content.Context; 22 | import android.os.Build.VERSION_CODES; 23 | import android.os.Bundle; 24 | import com.afwsamples.testdpc.DeviceAdminReceiver; 25 | import com.afwsamples.testdpc.R; 26 | import com.afwsamples.testdpc.common.SelectAppFragment; 27 | 28 | /** 29 | * This fragment provides functionalities related to delegated certificate installer. These include 30 | * 1) {@link DevicePolicyManager#setCertInstallerPackage} 2) {@link 31 | * DevicePolicyManager#getCertInstallerPackage} 32 | */ 33 | @TargetApi(VERSION_CODES.M) 34 | public class DelegatedCertInstallerFragment extends SelectAppFragment { 35 | 36 | private DevicePolicyManager mDpm; 37 | 38 | @Override 39 | public void onCreate(Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | mDpm = (DevicePolicyManager) getContext().getSystemService(Context.DEVICE_POLICY_SERVICE); 42 | } 43 | 44 | @Override 45 | public void onResume() { 46 | super.onResume(); 47 | getActivity().getActionBar().setTitle(R.string.manage_cert_installer); 48 | } 49 | 50 | @Override 51 | protected void setSelectedPackage(String pkgName) { 52 | mDpm.setCertInstallerPackage(DeviceAdminReceiver.getComponentName(getActivity()), pkgName); 53 | } 54 | 55 | @Override 56 | protected void clearSelectedPackage() { 57 | mDpm.setCertInstallerPackage(DeviceAdminReceiver.getComponentName(getActivity()), null); 58 | } 59 | 60 | @Override 61 | protected String getSelectedPackage() { 62 | return mDpm.getCertInstallerPackage(DeviceAdminReceiver.getComponentName(getActivity())); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/policy/keymanagement/KeyGenerationParameters.java: -------------------------------------------------------------------------------- 1 | package com.afwsamples.testdpc.policy.keymanagement; 2 | 3 | public class KeyGenerationParameters { 4 | public final String alias; 5 | public final boolean isUserSelectable; 6 | public final byte[] attestationChallenge; 7 | public final int idAttestationFlags; 8 | public final boolean useStrongBox; 9 | public final boolean generateEcKey; 10 | 11 | public KeyGenerationParameters( 12 | String alias, 13 | boolean isUserSelectable, 14 | byte[] attestationChallenge, 15 | int idAttestationFlags, 16 | boolean useStrongBox, 17 | boolean generateEcKey) { 18 | this.alias = alias; 19 | this.isUserSelectable = isUserSelectable; 20 | this.attestationChallenge = attestationChallenge; 21 | this.idAttestationFlags = idAttestationFlags; 22 | this.useStrongBox = useStrongBox; 23 | this.generateEcKey = generateEcKey; 24 | } 25 | 26 | public static class Builder { 27 | private String mAlias; 28 | private boolean mIsUserSelectable; 29 | private byte[] mAttestationChallenge; 30 | private int mIdAttestationFlags; 31 | private boolean mUseStrongBox; 32 | private boolean mGenerateEcKey; 33 | 34 | public Builder setAlias(String alias) { 35 | mAlias = alias; 36 | return this; 37 | } 38 | 39 | public Builder setIsUserSelectable(boolean isUserSelectable) { 40 | mIsUserSelectable = isUserSelectable; 41 | return this; 42 | } 43 | 44 | public Builder setAttestationChallenge(byte[] attestationChallenge) { 45 | mAttestationChallenge = attestationChallenge; 46 | return this; 47 | } 48 | 49 | public Builder setIdAttestationFlags(int idAttestationFlags) { 50 | mIdAttestationFlags = idAttestationFlags; 51 | return this; 52 | } 53 | 54 | public Builder setUseStrongBox(boolean useStrongBox) { 55 | mUseStrongBox = useStrongBox; 56 | return this; 57 | } 58 | 59 | public Builder setGenerateEcKey(boolean generateEcKey) { 60 | mGenerateEcKey = generateEcKey; 61 | return this; 62 | } 63 | 64 | public KeyGenerationParameters build() { 65 | return new KeyGenerationParameters( 66 | mAlias, 67 | mIsUserSelectable, 68 | mAttestationChallenge, 69 | mIdAttestationFlags, 70 | mUseStrongBox, 71 | mGenerateEcKey); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/policy/keymanagement/ShowToastCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc.policy.keymanagement; 18 | 19 | public interface ShowToastCallback { 20 | void showToast(int msgId, Object... args); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/policy/utils/AttestationPackageInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | 15 | package com.afwsamples.testdpc.policy.utils; 16 | 17 | import java.io.UnsupportedEncodingException; 18 | import java.security.cert.CertificateParsingException; 19 | import org.bouncycastle.asn1.ASN1Encodable; 20 | import org.bouncycastle.asn1.ASN1Sequence; 21 | 22 | @SuppressWarnings("EqualsHashCode") 23 | public class AttestationPackageInfo implements java.lang.Comparable { 24 | private static final int PACKAGE_NAME_INDEX = 0; 25 | private static final int VERSION_INDEX = 1; 26 | 27 | private final String packageName; 28 | private final long version; 29 | 30 | public AttestationPackageInfo(String packageName, long version) { 31 | this.packageName = packageName; 32 | this.version = version; 33 | } 34 | 35 | public AttestationPackageInfo(ASN1Encodable asn1Encodable) throws CertificateParsingException { 36 | if (!(asn1Encodable instanceof ASN1Sequence)) { 37 | throw new CertificateParsingException( 38 | "Expected sequence for AttestationPackageInfo, found " 39 | + asn1Encodable.getClass().getName()); 40 | } 41 | 42 | ASN1Sequence sequence = (ASN1Sequence) asn1Encodable; 43 | try { 44 | packageName = 45 | Asn1Utils.getStringFromAsn1OctetStreamAssumingUTF8( 46 | sequence.getObjectAt(PACKAGE_NAME_INDEX)); 47 | } catch (UnsupportedEncodingException e) { 48 | throw new CertificateParsingException( 49 | "Converting octet stream to String triggered an UnsupportedEncodingException", e); 50 | } 51 | version = Asn1Utils.getLongFromAsn1(sequence.getObjectAt(VERSION_INDEX)); 52 | } 53 | 54 | public String getPackageName() { 55 | return packageName; 56 | } 57 | 58 | public long getVersion() { 59 | return version; 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return new StringBuilder() 65 | .append("Package name: ") 66 | .append(getPackageName()) 67 | .append("\nVersion: " + getVersion()) 68 | .toString(); 69 | } 70 | 71 | @Override 72 | public int compareTo(AttestationPackageInfo other) { 73 | int res = packageName.compareTo(other.packageName); 74 | if (res != 0) return res; 75 | res = Long.compare(version, other.version); 76 | if (res != 0) return res; 77 | return res; 78 | } 79 | 80 | @Override 81 | public boolean equals(Object o) { 82 | return (o instanceof AttestationPackageInfo) && (0 == compareTo((AttestationPackageInfo) o)); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/profilepolicy/apprestrictions/AppRestrictionsProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc.profilepolicy.apprestrictions; 18 | 19 | import android.app.Service; 20 | import android.app.admin.DevicePolicyManager; 21 | import android.content.ComponentName; 22 | import android.content.Intent; 23 | import android.os.Bundle; 24 | import android.os.IBinder; 25 | import android.os.Messenger; 26 | import com.afwsamples.testdpc.DeviceAdminReceiver; 27 | 28 | /** 29 | * Before N, only the DPC has permission to set application restrictions via {@link 30 | * DevicePolicyManager#setApplicationRestrictions(ComponentName, String, Bundle)}. 31 | * 32 | *

To enable the another package to manage the application restrictions, a bound service is used 33 | * to pass them to {@link AppRestrictionsProxy}. 34 | * 35 | *

From N onwards, a given package can be granted permission to manage application restrictions, 36 | * which removes the need for the proxy code. 37 | */ 38 | public class AppRestrictionsProxy extends Service { 39 | 40 | private Messenger mMessenger; 41 | 42 | @Override 43 | public void onCreate() { 44 | mMessenger = 45 | new Messenger( 46 | new AppRestrictionsProxyHandler(this, DeviceAdminReceiver.getComponentName(this))); 47 | } 48 | 49 | @Override 50 | public IBinder onBind(Intent intent) { 51 | return mMessenger.getBinder(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/provision/CheckInState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc.provision; 18 | 19 | import android.content.Context; 20 | import android.content.Intent; 21 | import android.content.SharedPreferences; 22 | import android.preference.PreferenceManager; 23 | import androidx.localbroadcastmanager.content.LocalBroadcastManager; 24 | 25 | public class CheckInState { 26 | private SharedPreferences mSharedPreferences; 27 | private Context mContext; 28 | 29 | public static final int FIRST_ACCOUNT_STATE_PENDING = 0; 30 | public static final int FIRST_ACCOUNT_STATE_READY = 1; 31 | public static final int FIRST_ACCOUNT_STATE_TIMEOUT = 2; 32 | 33 | private static final String KEY_FIRST_ACCOUNT_STATE = "first_account_state"; 34 | 35 | /** Broadcast Action: FIRST_ACCOUNT_READY broadcast is processed. */ 36 | public static final String FIRST_ACCOUNT_READY_PROCESSED_ACTION = 37 | "com.afwsamples.testdpc.FIRST_ACCOUNT_READY_PROCESSED"; 38 | 39 | public CheckInState(Context context) { 40 | mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); 41 | mContext = context.getApplicationContext(); 42 | } 43 | 44 | public int getFirstAccountState() { 45 | return mSharedPreferences.getInt(KEY_FIRST_ACCOUNT_STATE, FIRST_ACCOUNT_STATE_PENDING); 46 | } 47 | 48 | public void setFirstAccountState(int state) { 49 | mSharedPreferences.edit().putInt(KEY_FIRST_ACCOUNT_STATE, state).apply(); 50 | if (state != FIRST_ACCOUNT_STATE_PENDING) { 51 | LocalBroadcastManager.getInstance(mContext) 52 | .sendBroadcast(new Intent(FIRST_ACCOUNT_READY_PROCESSED_ACTION)); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/provision/ProvisioningSuccessActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc.provision; 18 | 19 | import android.app.Activity; 20 | import android.content.Intent; 21 | import android.os.Bundle; 22 | import android.util.Log; 23 | import android.widget.Toast; 24 | import com.afwsamples.testdpc.R; 25 | 26 | /** 27 | * Activity that gets launched by the {@link 28 | * android.app.admin.DevicePolicyManager#ACTION_PROVISIONING_SUCCESSFUL} intent. 29 | */ 30 | public class ProvisioningSuccessActivity extends Activity { 31 | private static final String TAG = "ProvisioningSuccess"; 32 | 33 | @Override 34 | public void onCreate(Bundle icicle) { 35 | super.onCreate(icicle); 36 | 37 | PostProvisioningTask task = new PostProvisioningTask(this); 38 | if (!task.performPostProvisioningOperations(getIntent())) { 39 | finish(); 40 | return; 41 | } 42 | 43 | Intent launchIntent = task.getPostProvisioningLaunchIntent(getIntent()); 44 | if (launchIntent != null) { 45 | startActivity(launchIntent); 46 | } else { 47 | Log.e(TAG, "ProvisioningSuccessActivity.onCreate() invoked, but ownership " + "not assigned"); 48 | Toast.makeText(this, R.string.device_admin_receiver_failure, Toast.LENGTH_LONG).show(); 49 | } 50 | finish(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/provision/ProvisioningUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.afwsamples.testdpc.provision; 18 | 19 | import android.app.admin.DevicePolicyManager; 20 | import android.content.ComponentName; 21 | import android.content.Context; 22 | import com.afwsamples.testdpc.DeviceAdminReceiver; 23 | import com.afwsamples.testdpc.R; 24 | import com.afwsamples.testdpc.common.ReflectionUtil; 25 | 26 | public class ProvisioningUtil { 27 | 28 | public static void enableProfile(Context context) { 29 | DevicePolicyManager manager = 30 | (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); 31 | ComponentName componentName = DeviceAdminReceiver.getReceiverComponentName(context); 32 | // This is the name for the newly created managed profile. 33 | manager.setProfileName(componentName, context.getString(R.string.profile_name)); 34 | // We enable the profile here. 35 | manager.setProfileEnabled(componentName); 36 | } 37 | 38 | /** 39 | * Helper method used to automatically provision the device, which is useful in demos. 40 | */ 41 | public static boolean isAutoProvisioningDeviceOwnerMode() { 42 | String prop = ReflectionUtil.getSystemProperty("dev.tmp.testdpc.auto_provision_mode"); 43 | return "do".equals(prop); 44 | } 45 | 46 | private ProvisioningUtil() { 47 | throw new UnsupportedOperationException("provides only static methods"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/search/BaseIndexableFragment.java: -------------------------------------------------------------------------------- 1 | package com.afwsamples.testdpc.search; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import com.afwsamples.testdpc.common.BaseSearchablePolicyPreferenceFragment; 6 | import java.util.List; 7 | 8 | public abstract class BaseIndexableFragment { 9 | private static final String TAG = "BaseIndexableFragment"; 10 | protected String fragmentName; 11 | 12 | public BaseIndexableFragment( 13 | Class fragmentClass) { 14 | this.fragmentName = fragmentClass.getName(); 15 | } 16 | 17 | @SuppressWarnings("unchecked") 18 | public boolean isAvailable(Context context) { 19 | try { 20 | @SuppressWarnings("unchecked") 21 | Class clazz = 22 | (Class) Class.forName(this.fragmentName); 23 | BaseSearchablePolicyPreferenceFragment fragment = clazz.newInstance(); 24 | return fragment.isAvailable(context); 25 | } catch (ClassNotFoundException 26 | | java.lang.InstantiationException 27 | | IllegalStateException 28 | | IllegalAccessException e) { 29 | Log.e(TAG, "isAvailable error", e); 30 | } 31 | return false; 32 | } 33 | 34 | public abstract List index(Context context); 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/search/IndexableFragments.java: -------------------------------------------------------------------------------- 1 | package com.afwsamples.testdpc.search; 2 | 3 | import com.afwsamples.testdpc.R; 4 | import com.afwsamples.testdpc.common.BaseSearchablePolicyPreferenceFragment; 5 | import com.afwsamples.testdpc.comp.BindDeviceAdminFragment; 6 | import com.afwsamples.testdpc.policy.OverrideApnFragment; 7 | import com.afwsamples.testdpc.policy.PolicyManagementFragment; 8 | import com.afwsamples.testdpc.policy.keyguard.LockScreenPolicyFragment; 9 | import com.afwsamples.testdpc.policy.keyguard.PasswordConstraintsFragment; 10 | import com.afwsamples.testdpc.profilepolicy.ProfilePolicyManagementFragment; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * Stores all the indexable fragments. 16 | * 17 | *

To index a newly added fragment, there are only two things needed to be done. Make you 18 | * fragment extends {@link BaseSearchablePolicyPreferenceFragment} and add it to this class. 19 | */ 20 | public class IndexableFragments { 21 | private static final List sIndexableFragments = new ArrayList<>(); 22 | 23 | static { 24 | sIndexableFragments.add( 25 | new XmlIndexableFragment(PolicyManagementFragment.class, R.xml.device_policy_header)); 26 | sIndexableFragments.add( 27 | new XmlIndexableFragment( 28 | ProfilePolicyManagementFragment.class, R.xml.profile_policy_header)); 29 | sIndexableFragments.add( 30 | new XmlIndexableFragment(LockScreenPolicyFragment.class, R.xml.lock_screen_preferences)); 31 | sIndexableFragments.add( 32 | new XmlIndexableFragment( 33 | PasswordConstraintsFragment.class, R.xml.password_constraint_preferences)); 34 | sIndexableFragments.add( 35 | new XmlIndexableFragment(BindDeviceAdminFragment.class, R.xml.bind_device_admin_policies)); 36 | sIndexableFragments.add(new UserRestrictionIndexableFragment()); 37 | sIndexableFragments.add( 38 | new XmlIndexableFragment(OverrideApnFragment.class, R.xml.override_apn_preferences)); 39 | } 40 | 41 | public static List values() { 42 | return new ArrayList<>(sIndexableFragments); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/search/PreferenceCrawler.java: -------------------------------------------------------------------------------- 1 | package com.afwsamples.testdpc.search; 2 | 3 | import android.content.Context; 4 | import android.util.TimingLogger; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * Crawl indexable fragments to index all their preferences. Run adb shell setprop 10 | * log.tag.PreferenceCrawler_Timer VERBOSE to see timing log. At the time of writing, nexus 5x 11 | * spends 27ms to finish crawling. 12 | */ 13 | public class PreferenceCrawler { 14 | private Context mContext; 15 | private static final String TAG = "PreferenceCrawler_Timer"; 16 | 17 | public PreferenceCrawler(Context context) { 18 | mContext = context; 19 | } 20 | 21 | public List doCrawl() { 22 | final TimingLogger logger = new TimingLogger(TAG, "doCrawl"); 23 | List indexablePreferences = new ArrayList<>(); 24 | List indexableFragments = IndexableFragments.values(); 25 | for (BaseIndexableFragment indexableFragment : indexableFragments) { 26 | indexablePreferences.addAll(indexableFragment.index(mContext)); 27 | logger.addSplit("processed " + indexableFragment.fragmentName); 28 | } 29 | logger.addSplit("Finish crawling"); 30 | logger.dumpToLog(); 31 | return indexablePreferences; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/search/PreferenceIndex.java: -------------------------------------------------------------------------------- 1 | package com.afwsamples.testdpc.search; 2 | 3 | /** Represent index of a preference object. */ 4 | public class PreferenceIndex { 5 | 6 | /** Key of preference. */ 7 | public String key; 8 | /** Title of preference. */ 9 | public String title; 10 | /** Class of fragment holding the preference. */ 11 | public String fragmentClass; 12 | 13 | public PreferenceIndex(String key, String title, String fragmentClass) { 14 | this.key = key; 15 | this.title = title; 16 | this.fragmentClass = fragmentClass; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/search/PreferenceXmlUtil.java: -------------------------------------------------------------------------------- 1 | package com.afwsamples.testdpc.search; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.util.AttributeSet; 6 | import android.util.TypedValue; 7 | 8 | /** 9 | * Util class to retrieve some values of attributes in preference xml. To achieve this, we need to: 10 | * 1. Obtain the resource id of certain attributes that we care such as title and key. 2. Obtain the 11 | * value of those attribute {@link TypedArray#peekValue(int)}. 12 | */ 13 | public class PreferenceXmlUtil { 14 | 15 | public static String getDataTitle(Context context, AttributeSet attrs) 16 | throws ReflectiveOperationException { 17 | return getData(context, attrs, android.R.attr.title); 18 | } 19 | 20 | public static String getDataKey(Context context, AttributeSet attrs) 21 | throws ReflectiveOperationException { 22 | return getData(context, attrs, android.R.attr.key); 23 | } 24 | 25 | private static String getData(Context context, AttributeSet set, int attribute) 26 | throws ReflectiveOperationException { 27 | final TypedArray sa = context.obtainStyledAttributes(set, new int[] {attribute}); 28 | try { 29 | final TypedValue tv = sa.peekValue(0); 30 | CharSequence data = null; 31 | if (tv != null && tv.type == TypedValue.TYPE_STRING) { 32 | if (tv.resourceId != 0) { 33 | data = context.getText(tv.resourceId); 34 | } else { 35 | data = tv.string; 36 | } 37 | } 38 | return (data != null) ? data.toString() : null; 39 | } finally { 40 | sa.recycle(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/search/SearchItemAdapter.java: -------------------------------------------------------------------------------- 1 | package com.afwsamples.testdpc.search; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.TextView; 7 | import androidx.recyclerview.widget.RecyclerView; 8 | import com.afwsamples.testdpc.R; 9 | import com.afwsamples.testdpc.search.SearchItemAdapter.SearchItemViewHolder; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | /** Represent rows of search result in {@link PolicySearchFragment}. */ 14 | public class SearchItemAdapter extends RecyclerView.Adapter { 15 | private List mPreferenceIndexList = new ArrayList<>(); 16 | private OnItemClickListener mOnItemClickListener; 17 | 18 | public SearchItemAdapter(OnItemClickListener onItemClickListener) { 19 | mOnItemClickListener = onItemClickListener; 20 | } 21 | 22 | @Override 23 | public SearchItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 24 | View itemView = 25 | LayoutInflater.from(parent.getContext()) 26 | .inflate(R.layout.search_result_item, parent, false); 27 | return new SearchItemViewHolder(itemView); 28 | } 29 | 30 | @Override 31 | public void onBindViewHolder(final SearchItemViewHolder holder, int position) { 32 | final PreferenceIndex preferenceIndex = mPreferenceIndexList.get(position); 33 | holder.textView.setText(preferenceIndex.title); 34 | holder.textView.setOnClickListener( 35 | new View.OnClickListener() { 36 | @Override 37 | public void onClick(View view) { 38 | final int adapterPosition = holder.getAdapterPosition(); 39 | PreferenceIndex clickedItem = mPreferenceIndexList.get(adapterPosition); 40 | mOnItemClickListener.onItemClick(clickedItem); 41 | } 42 | }); 43 | } 44 | 45 | @Override 46 | public int getItemCount() { 47 | return mPreferenceIndexList.size(); 48 | } 49 | 50 | public void setSearchResult(List list) { 51 | mPreferenceIndexList = list; 52 | } 53 | 54 | public interface OnItemClickListener { 55 | void onItemClick(PreferenceIndex preferenceIndex); 56 | } 57 | 58 | public static class SearchItemViewHolder extends RecyclerView.ViewHolder { 59 | public TextView textView; 60 | 61 | public SearchItemViewHolder(View itemView) { 62 | super(itemView); 63 | textView = (TextView) itemView; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/search/UserRestrictionIndexableFragment.java: -------------------------------------------------------------------------------- 1 | package com.afwsamples.testdpc.search; 2 | 3 | import android.content.Context; 4 | import com.afwsamples.testdpc.policy.UserRestriction; 5 | import com.afwsamples.testdpc.policy.UserRestrictionsDisplayFragment; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class UserRestrictionIndexableFragment extends BaseIndexableFragment { 10 | public UserRestrictionIndexableFragment() { 11 | super(UserRestrictionsDisplayFragment.class); 12 | } 13 | 14 | @Override 15 | public List index(Context context) { 16 | List preferenceIndices = new ArrayList<>(); 17 | for (UserRestriction userRestriction : UserRestriction.ALL_USER_RESTRICTIONS) { 18 | preferenceIndices.add( 19 | new PreferenceIndex( 20 | userRestriction.key, context.getString(userRestriction.titleResId), fragmentName)); 21 | } 22 | return preferenceIndices; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/util/LooperExecutor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | package com.afwsamples.testdpc.util; 17 | 18 | import android.os.Handler; 19 | import android.os.Looper; 20 | import java.util.List; 21 | import java.util.concurrent.AbstractExecutorService; 22 | import java.util.concurrent.TimeUnit; 23 | 24 | /** Extension of {@link AbstractExecutorService} which executed on a provided looper. */ 25 | public class LooperExecutor extends AbstractExecutorService { 26 | 27 | private final Handler mHandler; 28 | 29 | public LooperExecutor(Looper looper) { 30 | mHandler = new Handler(looper); 31 | } 32 | 33 | @Override 34 | public void execute(Runnable runnable) { 35 | if (mHandler.getLooper() == Looper.myLooper()) { 36 | runnable.run(); 37 | } else { 38 | mHandler.post(runnable); 39 | } 40 | } 41 | 42 | /** Not supported and throws an exception when used. */ 43 | @Override 44 | public void shutdown() { 45 | throw new UnsupportedOperationException(); 46 | } 47 | 48 | /** Not supported and throws an exception when used. */ 49 | @Override 50 | @Deprecated 51 | public List shutdownNow() { 52 | throw new UnsupportedOperationException(); 53 | } 54 | 55 | @Override 56 | public boolean isShutdown() { 57 | return false; 58 | } 59 | 60 | @Override 61 | public boolean isTerminated() { 62 | return false; 63 | } 64 | 65 | /** Not supported and throws an exception when used. */ 66 | @Override 67 | public boolean awaitTermination(long l, TimeUnit timeUnit) throws InterruptedException { 68 | throw new UnsupportedOperationException(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/afwsamples/testdpc/util/MainThreadExecutor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | 17 | package com.afwsamples.testdpc.util; 18 | 19 | import android.os.Looper; 20 | 21 | /** 22 | * An executor service that executes its tasks on the main thread. 23 | * 24 | *

Shutting down this executor is not supported. 25 | */ 26 | public class MainThreadExecutor extends LooperExecutor { 27 | 28 | public MainThreadExecutor() { 29 | super(Looper.getMainLooper()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/android-testdpc/305c86d36e9b7c85e41e9e01fbf2d648fa4970fb/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/main/res/drawable-hdpi/ic_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/android-testdpc/305c86d36e9b7c85e41e9e01fbf2d648fa4970fb/src/main/res/drawable-hdpi/ic_search.png -------------------------------------------------------------------------------- /src/main/res/drawable-hdpi/setup_illustration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/android-testdpc/305c86d36e9b7c85e41e9e01fbf2d648fa4970fb/src/main/res/drawable-hdpi/setup_illustration.png -------------------------------------------------------------------------------- /src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/android-testdpc/305c86d36e9b7c85e41e9e01fbf2d648fa4970fb/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/main/res/drawable-mdpi/ic_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/android-testdpc/305c86d36e9b7c85e41e9e01fbf2d648fa4970fb/src/main/res/drawable-mdpi/ic_search.png -------------------------------------------------------------------------------- /src/main/res/drawable-mdpi/setup_illustration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/android-testdpc/305c86d36e9b7c85e41e9e01fbf2d648fa4970fb/src/main/res/drawable-mdpi/setup_illustration.png -------------------------------------------------------------------------------- /src/main/res/drawable-sw600dp-hdpi/setup_illustration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/android-testdpc/305c86d36e9b7c85e41e9e01fbf2d648fa4970fb/src/main/res/drawable-sw600dp-hdpi/setup_illustration.png -------------------------------------------------------------------------------- /src/main/res/drawable-sw600dp-mdpi/setup_illustration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/android-testdpc/305c86d36e9b7c85e41e9e01fbf2d648fa4970fb/src/main/res/drawable-sw600dp-mdpi/setup_illustration.png -------------------------------------------------------------------------------- /src/main/res/drawable-sw600dp-xhdpi/setup_illustration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/android-testdpc/305c86d36e9b7c85e41e9e01fbf2d648fa4970fb/src/main/res/drawable-sw600dp-xhdpi/setup_illustration.png -------------------------------------------------------------------------------- /src/main/res/drawable-sw600dp-xxhdpi/setup_illustration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/android-testdpc/305c86d36e9b7c85e41e9e01fbf2d648fa4970fb/src/main/res/drawable-sw600dp-xxhdpi/setup_illustration.png -------------------------------------------------------------------------------- /src/main/res/drawable-sw600dp-xxxhdpi/setup_illustration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/android-testdpc/305c86d36e9b7c85e41e9e01fbf2d648fa4970fb/src/main/res/drawable-sw600dp-xxxhdpi/setup_illustration.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/android-testdpc/305c86d36e9b7c85e41e9e01fbf2d648fa4970fb/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/android-testdpc/305c86d36e9b7c85e41e9e01fbf2d648fa4970fb/src/main/res/drawable-xhdpi/ic_search.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/setup_illustration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/android-testdpc/305c86d36e9b7c85e41e9e01fbf2d648fa4970fb/src/main/res/drawable-xhdpi/setup_illustration.png -------------------------------------------------------------------------------- /src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/android-testdpc/305c86d36e9b7c85e41e9e01fbf2d648fa4970fb/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/main/res/drawable-xxhdpi/ic_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/android-testdpc/305c86d36e9b7c85e41e9e01fbf2d648fa4970fb/src/main/res/drawable-xxhdpi/ic_search.png -------------------------------------------------------------------------------- /src/main/res/drawable-xxhdpi/setup_illustration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/android-testdpc/305c86d36e9b7c85e41e9e01fbf2d648fa4970fb/src/main/res/drawable-xxhdpi/setup_illustration.png -------------------------------------------------------------------------------- /src/main/res/drawable-xxxhdpi/setup_illustration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlesamples/android-testdpc/305c86d36e9b7c85e41e9e01fbf2d648fa4970fb/src/main/res/drawable-xxxhdpi/setup_illustration.png -------------------------------------------------------------------------------- /src/main/res/drawable/arrow_down.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/res/drawable/arrow_up_down.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 21 | 22 | 27 | 28 | -------------------------------------------------------------------------------- /src/main/res/drawable/ic_circle.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /src/main/res/drawable/ic_enterprise_blue.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 13 | -------------------------------------------------------------------------------- /src/main/res/layout/account_row.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 23 | 24 | 30 | 31 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/main/res/layout/activity_add_account.xml: -------------------------------------------------------------------------------- 1 | 16 | 24 | 25 | 30 | 31 | 36 | 37 | 43 | 44 | 49 | 50 | 55 | 56 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 25 | -------------------------------------------------------------------------------- /src/main/res/layout/add_intent_or_intent_filter.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 20 | 21 | 23 | 24 | 30 | 36 | 40 | 41 | 47 | 48 | 53 |