diff options
16 files changed, 976 insertions, 0 deletions
diff --git a/samples/training/device-management-policy/AndroidManifest.xml b/samples/training/device-management-policy/AndroidManifest.xml new file mode 100644 index 000000000..30c15c45f --- /dev/null +++ b/samples/training/device-management-policy/AndroidManifest.xml @@ -0,0 +1,44 @@ +<!-- + Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.training.deviceadmin" + android:versionCode="1" + android:versionName="1.0"> + + <uses-sdk android:minSdkVersion="8" + android:targetSdkVersion="14" /> + + <application android:label="@string/app_name"> + <activity android:name=".PolicySetupActivity" android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity android:name="com.example.training.deviceadmin.SecureActivity" + android:label="@string/app_name"/> + <receiver android:name="com.example.training.deviceadmin.Policy$PolicyAdmin" + android:label="@string/app_policy_admin_name" + android:permission="android.permission.BIND_DEVICE_ADMIN"> + <meta-data android:name="android.app.device_admin" + android:resource="@xml/device_admin" /> + <intent-filter> + <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" /> + </intent-filter> + </receiver> + </application> +</manifest> diff --git a/samples/training/device-management-policy/_index.html b/samples/training/device-management-policy/_index.html new file mode 100755 index 000000000..7799330e7 --- /dev/null +++ b/samples/training/device-management-policy/_index.html @@ -0,0 +1,3 @@ +<p>This is a security-aware sample application that demonstrates the enforcement of device administration policies on Android 2.2 or above platforms.</p> + +<p>Refer to the <a href="/training/enterprise/device-management-policy.html" />Enhancing Security with Device Management Policies</a> class for detail. diff --git a/samples/training/device-management-policy/res/layout/activity_policy_setup.xml b/samples/training/device-management-policy/res/layout/activity_policy_setup.xml new file mode 100644 index 000000000..cf36ba094 --- /dev/null +++ b/samples/training/device-management-policy/res/layout/activity_policy_setup.xml @@ -0,0 +1,93 @@ +<!-- + Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="vertical"> + <ScrollView android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_weight="1" > + + <LinearLayout android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingTop="@dimen/top_padding" + android:paddingLeft="@dimen/side_padding" + android:paddingRight="@dimen/side_padding" > + <TextView android:id="@+id/policy_instructions" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/setup_policy_instruction" + android:textSize="@dimen/text_size_medium" /> + <!-- Password type --> + <TextView android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/setup_password_quality" + android:textSize="@dimen/text_size_small" + android:paddingTop="3dp" /> + <Spinner android:id="@+id/policy_password_quality" + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:prompt="@string/setup_password_quality" + android:paddingBottom="3dp" + android:focusable="true" > + <requestFocus/> + </Spinner> + <!-- Minimum password length --> + <TextView android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/setup_password_length" + android:textSize="@dimen/text_size_small" + android:paddingTop="3dp" /> + <EditText android:id="@+id/policy_password_length" + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:hint="@string/setup_password_length" + android:inputType="number" /> + <!-- Minimum password uppercase characters --> + <LinearLayout android:id="@+id/password_uppercase_view" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:visibility="gone" > + <TextView android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/setup_password_uppercase" + android:textSize="@dimen/text_size_small" + android:paddingTop="3dp" /> + <EditText android:id="@+id/policy_password_uppercase" + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:hint="@string/setup_password_uppercase" + android:inputType="number" /> + </LinearLayout> + </LinearLayout> + </ScrollView> + <TextView android:id="@+id/setup_message" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:textSize="@dimen/text_size_medium" + android:paddingBottom="5dp" + android:paddingLeft="@dimen/side_padding" + android:paddingRight="@dimen/side_padding" /> + <Button android:id="@+id/setup_action_btn" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:minWidth="200dp" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="@dimen/top_padding" /> +</LinearLayout> diff --git a/samples/training/device-management-policy/res/layout/activity_secure.xml b/samples/training/device-management-policy/res/layout/activity_secure.xml new file mode 100644 index 000000000..779057462 --- /dev/null +++ b/samples/training/device-management-policy/res/layout/activity_secure.xml @@ -0,0 +1,26 @@ +<!-- + Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="vertical"> + <TextView android:layout_height="wrap_content" + android:text="@string/secure_message" + android:textSize="@dimen/text_size_large" + android:layout_width="wrap_content" + android:gravity="center_vertical|center_horizontal" /> +</LinearLayout> diff --git a/samples/training/device-management-policy/res/layout/activity_view_policy.xml b/samples/training/device-management-policy/res/layout/activity_view_policy.xml new file mode 100644 index 000000000..5a7c21418 --- /dev/null +++ b/samples/training/device-management-policy/res/layout/activity_view_policy.xml @@ -0,0 +1,89 @@ +<!-- + Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="vertical"> + <ScrollView android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_weight="1" > + + <LinearLayout android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingTop="@dimen/top_padding" + android:paddingLeft="@dimen/side_padding" + android:paddingRight="@dimen/side_padding" > + <TextView android:id="@+id/policy_instructions" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/view_policy_instruction" + android:textSize="@dimen/text_size_medium" /> + <!-- Password type --> + <TextView android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/view_password_quality" + android:textSize="@dimen/text_size_small" + android:paddingTop="3dp" /> + <TextView android:id="@+id/policy_password_quality" + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:paddingBottom="3dp" + android:paddingLeft="3dp" /> + <!-- Minimum password length --> + <TextView android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/view_password_length" + android:textSize="@dimen/text_size_small" + android:paddingTop="3dp" /> + <TextView android:id="@+id/policy_password_length" + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:paddingBottom="3dp" + android:paddingLeft="3dp" /> + <!-- Minimum password uppercase characters --> + <LinearLayout android:id="@+id/password_uppercase_view" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:visibility="gone" > + <TextView android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/view_password_uppercase" + android:textSize="@dimen/text_size_small" + android:paddingTop="3dp" /> + <TextView android:id="@+id/policy_password_uppercase" + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:paddingLeft="3dp" /> + </LinearLayout> + </LinearLayout> + </ScrollView> + <TextView android:id="@+id/setup_message" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:textSize="@dimen/text_size_medium" + android:paddingBottom="5dp" + android:paddingLeft="@dimen/side_padding" + android:paddingRight="@dimen/side_padding" /> + <Button android:id="@+id/setup_action_btn" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:minWidth="200dp" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="@dimen/top_padding" /> +</LinearLayout> diff --git a/samples/training/device-management-policy/res/values-v11/arrays.xml b/samples/training/device-management-policy/res/values-v11/arrays.xml new file mode 100644 index 000000000..45b23c1b7 --- /dev/null +++ b/samples/training/device-management-policy/res/values-v11/arrays.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <string-array name="password_types"> + <item>Anything</item> + <item>Something</item> + <item>Numeric</item> + <item>Alphabetic</item> + <item>Alphanumeric</item> + <item>Complex</item> + </string-array> +</resources> diff --git a/samples/training/device-management-policy/res/values-xlarge/dimens.xml b/samples/training/device-management-policy/res/values-xlarge/dimens.xml new file mode 100644 index 000000000..de75d95e1 --- /dev/null +++ b/samples/training/device-management-policy/res/values-xlarge/dimens.xml @@ -0,0 +1,26 @@ +<!-- + Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <!-- UI elements --> + <dimen name="top_padding">60dp</dimen> + <dimen name="side_padding">100dp</dimen> + + <!-- body content --> + <dimen name="text_size_small">18sp</dimen> + <dimen name="text_size_medium">24sp</dimen> + <dimen name="text_size_large">26sp</dimen> +</resources> diff --git a/samples/training/device-management-policy/res/values/arrays.xml b/samples/training/device-management-policy/res/values/arrays.xml new file mode 100644 index 000000000..09f3eac88 --- /dev/null +++ b/samples/training/device-management-policy/res/values/arrays.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <string-array name="password_types"> + <item>Anything</item> + <item>Something</item> + <item>Numeric</item> + <item>Alphabetic</item> + <item>Alphanumeric</item> + </string-array> +</resources>
\ No newline at end of file diff --git a/samples/training/device-management-policy/res/values/attrs.xml b/samples/training/device-management-policy/res/values/attrs.xml new file mode 100644 index 000000000..e781a0cc4 --- /dev/null +++ b/samples/training/device-management-policy/res/values/attrs.xml @@ -0,0 +1,50 @@ +<!-- + Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources> + + <declare-styleable name="AppTheme"> + <attr name="actionbarCompatTitleStyle" format="reference" /> + <attr name="actionbarCompatButtonStyle" format="reference" /> + <attr name="actionbarCompatButtonHomeStyle" format="reference" /> + <attr name="actionbarCompatProgressIndicatorStyle" format="reference" /> + </declare-styleable> + + <!-- Mostly taken from frameworks/base.git/core/res/res/values/attrs.xml --> + <declare-styleable name="TextAppearance"> + <attr name="textColor" format="color" /> + <attr name="textSize" format="dimension" /> + + <attr name="textStyle"> + <flag name="normal" value="0" /> + <flag name="bold" value="1" /> + <flag name="italic" value="2" /> + </attr> + + <attr name="typeface"> + <enum name="normal" value="0" /> + <enum name="sans" value="1" /> + <enum name="serif" value="2" /> + <enum name="monospace" value="3" /> + </attr> + + <attr name="shadowRadius" format="float" /> + <attr name="shadowDx" format="float" /> + <attr name="shadowDy" format="float" /> + <attr name="shadowColor" format="color" /> + </declare-styleable> + +</resources> diff --git a/samples/training/device-management-policy/res/values/colors.xml b/samples/training/device-management-policy/res/values/colors.xml new file mode 100644 index 000000000..8c3bec784 --- /dev/null +++ b/samples/training/device-management-policy/res/values/colors.xml @@ -0,0 +1,19 @@ +<!-- + Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources> + <color name="accent_1">#224894</color> +</resources> diff --git a/samples/training/device-management-policy/res/values/dimens.xml b/samples/training/device-management-policy/res/values/dimens.xml new file mode 100644 index 000000000..6f84ef6a4 --- /dev/null +++ b/samples/training/device-management-policy/res/values/dimens.xml @@ -0,0 +1,26 @@ +<!-- + Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources> + <!-- UI elements --> + <dimen name="top_padding">10dp</dimen> + <dimen name="side_padding">5dp</dimen> + + <!-- body content --> + <dimen name="text_size_small">14sp</dimen> + <dimen name="text_size_medium">18sp</dimen> + <dimen name="text_size_large">20sp</dimen> +</resources> diff --git a/samples/training/device-management-policy/res/values/strings.xml b/samples/training/device-management-policy/res/values/strings.xml new file mode 100644 index 000000000..5a37bd1d5 --- /dev/null +++ b/samples/training/device-management-policy/res/values/strings.xml @@ -0,0 +1,43 @@ +<!-- + Copyright (C) 2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources> + <string name="app_name">Device Admin Lesson</string> + <string name="app_policy_admin_name">Policy Admin Receiver</string> + + <!-- Setup activity --> + <string name="setup_policy_instruction">Set up device administration policy:</string> + <string name="setup_password_quality">Select a password quality</string> + <string name="setup_password_length">Set min. pw. length</string> + <string name="setup_password_uppercase">Set min. pw. uppercase</string> + <string name="setup_action_activate">Activate</string> + <string name="setup_message_activate">Press button to activate the device administrator</string> + <string name="setup_action_set_policy">Enforce Policy</string> + <string name="setup_message_set_policy">Press button to enforce specified policy.</string> + <string name="setup_action_enforce_policy">Update Password</string> + <string name="setup_message_enforce_policy">Current password doesn\'t conform to the policy.\nPress button to update password.</string> + <string name="setup_action_go_secured">Go</string> + <string name="setup_message_go_secured">Current password conforms to policy. Device is protected.\nPress button to access secured content.</string> + + <!-- View policy activity --> + <string name="view_policy_instruction">Review device administration policy:</string> + <string name="view_password_quality">Password quality:</string> + <string name="view_password_length">Minimum password length:</string> + <string name="view_password_uppercase">Minimum password upper case:</string> + + <string name="secure_message">Device is properly secured. Access is granted to secured content</string> + <string name="device_admin_activation_message">Provide a message here explaining why an application needs to become a device administrator.</string> +</resources> diff --git a/samples/training/device-management-policy/res/xml/device_admin.xml b/samples/training/device-management-policy/res/xml/device_admin.xml new file mode 100644 index 000000000..af1e66473 --- /dev/null +++ b/samples/training/device-management-policy/res/xml/device_admin.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2011 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<device-admin xmlns:android="http://schemas.android.com/apk/res/android"> + <uses-policies> + <limit-password /> + </uses-policies> +</device-admin>
\ No newline at end of file diff --git a/samples/training/device-management-policy/src/com/example/training/deviceadmin/Policy.java b/samples/training/device-management-policy/src/com/example/training/deviceadmin/Policy.java new file mode 100644 index 000000000..4cf63f5dc --- /dev/null +++ b/samples/training/device-management-policy/src/com/example/training/deviceadmin/Policy.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.training.deviceadmin; + +import android.app.Activity; +import android.app.admin.DeviceAdminReceiver; +import android.app.admin.DevicePolicyManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Build; + +public class Policy { + public static final int REQUEST_ADD_DEVICE_ADMIN = 1; + + private static final String APP_PREF = "APP_PREF"; + private static final String KEY_PASSWORD_LENGTH = "PW_LENGTH"; + private static final String KEY_PASSWORD_QUALITY = "PW_QUALITY"; + private static final String KEY_PASSWORD_MIN_UPPERCASE = "PW_MIN_UPPERCASE"; + + // Password quality values. This list must match the list + // found in res/values/arrays.xml + final static int[] PASSWORD_QUALITY_VALUES = new int[] { + DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, + DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, + DevicePolicyManager.PASSWORD_QUALITY_NUMERIC, + DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, + DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, + DevicePolicyManager.PASSWORD_QUALITY_COMPLEX + }; + + private int mPasswordQuality; + private int mPasswordLength; + private int mPasswordMinUpperCase; + private Context mContext; + private DevicePolicyManager mDPM; + private ComponentName mPolicyAdmin; + + public Policy(Context context) { + mContext = context; + mPasswordQuality = -1; + mPasswordLength = 0; + mPasswordMinUpperCase = 0; + mDPM = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); + mPolicyAdmin = new ComponentName(context, PolicyAdmin.class); + } + + /** + * Saves the policy parameters. + * + * @param passwordQuality Password quality. + * @param passwordLength Password minimum length. + * @param passwordUppercase Password minimum number of upper case alphabets. + */ + public void saveToLocal(int passwordQuality, int passwordLength, int passwordMinUppercase) { + SharedPreferences.Editor editor = + mContext.getSharedPreferences(APP_PREF, Context.MODE_PRIVATE).edit(); + if (mPasswordQuality != passwordQuality) { + editor.putInt(KEY_PASSWORD_QUALITY, passwordQuality); + mPasswordQuality = passwordQuality; + } + if (mPasswordLength != passwordLength) { + editor.putInt(KEY_PASSWORD_LENGTH, passwordLength); + mPasswordLength = passwordLength; + } + if (mPasswordMinUpperCase != passwordMinUppercase) { + editor.putInt(KEY_PASSWORD_MIN_UPPERCASE, passwordMinUppercase); + mPasswordMinUpperCase = passwordMinUppercase; + } + editor.commit(); + } + + public void readFromLocal() { + SharedPreferences prefs = mContext.getSharedPreferences(APP_PREF, Context.MODE_PRIVATE); + mPasswordQuality = prefs.getInt(KEY_PASSWORD_QUALITY, -1); + mPasswordLength = prefs.getInt(KEY_PASSWORD_LENGTH, -1); + mPasswordMinUpperCase = prefs.getInt(KEY_PASSWORD_MIN_UPPERCASE, -1); + } + + /** + * Getter for password quality. + * + * @return + */ + public int getPasswordQuality() { return mPasswordQuality; } + + /** + * Getter for password length. + * + * @return + */ + public int getPasswordLength() { return mPasswordLength; } + + /** + * Getter for password minimum upper case alphabets. + * + * @return + */ + public int getPasswordMinUpperCase() { return mPasswordMinUpperCase; } + + /** + * Getter for the policy administrator ComponentName object. + * + * @return + */ + public ComponentName getPolicyAdmin() { return mPolicyAdmin; } + + /** + * Indicates whether the device administrator is currently active. + * + * @return + */ + public boolean isAdminActive() { + return mDPM.isAdminActive(mPolicyAdmin); + } + + public boolean isActivePasswordSufficient() { + return mDPM.isActivePasswordSufficient(); + } + + public boolean isDeviceSecured() { + return isAdminActive() && isActivePasswordSufficient(); + } + + /** + * Configure policy defined in the object. + */ + public void configurePolicy() { + mDPM.setPasswordQuality(mPolicyAdmin, PASSWORD_QUALITY_VALUES[mPasswordQuality]); + mDPM.setPasswordMinimumLength(mPolicyAdmin, mPasswordLength); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + mDPM.setPasswordMinimumUpperCase(mPolicyAdmin, mPasswordMinUpperCase); + } + } + + /** + * Through the PolicyAdmin receiver, the app can use this to trap various device + * administration events, such as password change, incorrect password entry, etc. + * + */ + public static class PolicyAdmin extends DeviceAdminReceiver { + + @Override + public void onDisabled(Context context, Intent intent) { + // Called when the app is about to be deactivated as a device administrator. + // Deletes previously stored password policy. + super.onDisabled(context, intent); + SharedPreferences prefs = context.getSharedPreferences(APP_PREF, Activity.MODE_PRIVATE); + prefs.edit().clear().commit(); + } + } +} + diff --git a/samples/training/device-management-policy/src/com/example/training/deviceadmin/PolicySetupActivity.java b/samples/training/device-management-policy/src/com/example/training/deviceadmin/PolicySetupActivity.java new file mode 100644 index 000000000..8c7381bf2 --- /dev/null +++ b/samples/training/device-management-policy/src/com/example/training/deviceadmin/PolicySetupActivity.java @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.training.deviceadmin; + +import android.app.Activity; +import android.app.admin.DevicePolicyManager; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.Spinner; +import android.widget.TextView; + +/** + * Main Activity for the sample application. + * + * The Activity maintains and presents 2 different + * screens to the user -- a screen for configuring device administration policy and another screen + * for viewing configured policy. When it is detected that the screen-lock password satisfies + * the password strength required by the policy, the user is sent to an Activity containing + * protected content. + */ +public class PolicySetupActivity extends Activity { + private static final int REQ_ACTIVATE_DEVICE_ADMIN = 10; + private static final String SCREEN_ID_KEY = "LAYOUT_ID"; + + private static final String APP_PREF = "APP_PREF"; + private static final int UNKNOWN_SCREEN_ID = -1; + + // UI controls in policy setup screen. + private Spinner mPasswordQualityInputField; + private EditText mPasswordLengthInputField; + private EditText mPasswordMinUppercaseInputField; + + private Policy mPolicy; + private int mCurrentScreenId; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mPolicy = new Policy(this); + } + + @Override + protected void onResume() { + super.onResume(); + + SharedPreferences prefs = getSharedPreferences(APP_PREF, MODE_PRIVATE); + final int savedScreenId = prefs.getInt(SCREEN_ID_KEY, UNKNOWN_SCREEN_ID); + if (savedScreenId == UNKNOWN_SCREEN_ID || !mPolicy.isAdminActive()) { + setScreenContent(R.layout.activity_policy_setup); + } else { + setScreenContent(savedScreenId); + } + } + + private void setScreenContent(final int screenId) { + mCurrentScreenId = screenId; + setContentView(mCurrentScreenId); + getSharedPreferences(APP_PREF, MODE_PRIVATE).edit().putInt( + SCREEN_ID_KEY, mCurrentScreenId).commit(); + switch (mCurrentScreenId) { + case R.layout.activity_policy_setup: + initPolicySetupScreen(); + initNavigation(); + break; + case R.layout.activity_view_policy: + initViewPolicyScreen(); + initNavigation(); + break; + } + } + + @Override + protected void onPause() { + super.onPause(); + if (mCurrentScreenId == R.layout.activity_policy_setup) writePolicy(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == REQ_ACTIVATE_DEVICE_ADMIN && resultCode == RESULT_OK) { + // User just activated the application as a device administrator. + setScreenContent(mCurrentScreenId); + } else { + super.onActivityResult(requestCode, resultCode, data); + } + } + + @Override + public void onBackPressed() { + if (mCurrentScreenId == R.layout.activity_view_policy) { + setScreenContent(R.layout.activity_policy_setup); + return; + } + super.onBackPressed(); + } + + // Initialize policy set up screen. + private void initPolicySetupScreen() { + mPasswordQualityInputField = (Spinner) findViewById(R.id.policy_password_quality); + mPasswordLengthInputField = (EditText) findViewById(R.id.policy_password_length); + mPasswordMinUppercaseInputField = (EditText) findViewById(R.id.policy_password_uppercase); + ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, + R.array.password_types, android.R.layout.simple_spinner_item); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + mPasswordQualityInputField.setAdapter(adapter); + mPasswordQualityInputField.setOnItemSelectedListener( + new AdapterView.OnItemSelectedListener() { + public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { + LinearLayout passwordMinUppercaseView = + (LinearLayout) findViewById(R.id.password_uppercase_view); + // The minimum number of upper case field is only applicable for password + // qualities: alpha, alphanumeric, or complex. + if (pos > 2) + passwordMinUppercaseView.setVisibility(View.VISIBLE); + else + passwordMinUppercaseView.setVisibility(View.GONE); + } + + public void onNothingSelected(AdapterView<?> parent) {} + }); + + // Read previously saved policy and populate on the UI. + mPolicy.readFromLocal(); + mPasswordQualityInputField.setSelection(mPolicy.getPasswordQuality()); + if (mPolicy.getPasswordLength() > 0) { + mPasswordLengthInputField.setText(String.valueOf(mPolicy.getPasswordLength())); + } else { + mPasswordLengthInputField.setText(""); + } + + if (mPolicy.getPasswordMinUpperCase() > 0) { + mPasswordMinUppercaseInputField.setText( + String.valueOf(mPolicy.getPasswordMinUpperCase())); + } else { + mPasswordMinUppercaseInputField.setText(""); + } + } + + // Initialize policy viewing screen. + private void initViewPolicyScreen() { + TextView passwordQualityView = (TextView) findViewById(R.id.policy_password_quality); + TextView passwordLengthView = (TextView) findViewById(R.id.policy_password_length); + + // Read previously saved policy and populate on the UI. + mPolicy.readFromLocal(); + int passwordQualitySelection = mPolicy.getPasswordQuality(); + passwordQualityView.setText( + getResources().getStringArray(R.array.password_types)[passwordQualitySelection]); + passwordLengthView.setText(String.valueOf(mPolicy.getPasswordLength())); + if (passwordQualitySelection > 2) { + LinearLayout passwordMinUppercaseView = + (LinearLayout) findViewById(R.id.password_uppercase_view); + passwordMinUppercaseView.setVisibility(View.VISIBLE); + ((TextView) findViewById(R.id.policy_password_uppercase)).setText( + String.valueOf(mPolicy.getPasswordMinUpperCase())); + } + } + + // Set up navigation message and action button. + private void initNavigation() { + if (!mPolicy.isAdminActive()) { + // Activates device administrator. + setupNavigation(R.string.setup_message_activate, + R.string.setup_action_activate, + mActivateButtonListener); + } else if (mCurrentScreenId == R.layout.activity_policy_setup) { + setupNavigation(R.string.setup_message_set_policy, + R.string.setup_action_set_policy, + new View.OnClickListener() { + public void onClick(View view) { + writePolicy(); + mPolicy.configurePolicy(); + setScreenContent(R.layout.activity_view_policy); + } + }); + } + else if (!mPolicy.isActivePasswordSufficient()) { + // Launches password set-up screen in Settings. + setupNavigation(R.string.setup_message_enforce_policy, + R.string.setup_action_enforce_policy, + mEnforcePolicyListener); + } else { + // Grants access to secure content. + setupNavigation(R.string.setup_message_go_secured, + R.string.setup_action_go_secured, + new View.OnClickListener() { + public void onClick(View view) { + startActivity(new Intent(view.getContext(), SecureActivity.class)); + } + }); + } + } + + private View.OnClickListener mActivateButtonListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + // First, persist the policy. Then, launch intent to trigger the system screen + // requesting user to confirm the activation of the device administrator. + writePolicy(); + Intent activateDeviceAdminIntent = + new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN); + activateDeviceAdminIntent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, + mPolicy.getPolicyAdmin()); + // It is good practice to include the optional explanation text to explain to + // user why the application is requesting to be a device administrator. The system + // will display this message on the activation screen. + activateDeviceAdminIntent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, + getResources().getString(R.string.device_admin_activation_message)); + startActivityForResult(activateDeviceAdminIntent, REQ_ACTIVATE_DEVICE_ADMIN); + } + }; + + private View.OnClickListener mEnforcePolicyListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + writePolicy(); + // The device administration API does not "fix" the password if it is + // determined that the current password does not conform to what is requested + // by the policy. The caller is responsible for triggering the password set up + // screen via the below intent. + Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD); + startActivity(intent); + } + }; + + // Setup action button text and listener. + private void setupNavigation(int messageResId, int buttonTextResId, + View.OnClickListener listener) { + TextView setupMessage = (TextView) findViewById(R.id.setup_message); + setupMessage.setText(messageResId); + Button actionBtn = (Button) findViewById(R.id.setup_action_btn); + actionBtn.setText(buttonTextResId); + actionBtn.setOnClickListener(listener); + } + + // Save policy to shared preferences. + private void writePolicy() { + int passwordQuality = (int) mPasswordQualityInputField.getSelectedItemId(); + + int passwordLength = 0; + try { + passwordLength = Integer.valueOf(mPasswordLengthInputField.getText().toString()); + } catch (NumberFormatException nfe) {} // Defaults to 0. + + int passwordMinUppercase = 0; + try { + passwordMinUppercase = + Integer.valueOf(mPasswordMinUppercaseInputField.getText().toString()); + } catch (NumberFormatException nfe) {} // Defaults to 0. + + mPolicy.saveToLocal(passwordQuality, passwordLength, passwordMinUppercase); + } +} + diff --git a/samples/training/device-management-policy/src/com/example/training/deviceadmin/SecureActivity.java b/samples/training/device-management-policy/src/com/example/training/deviceadmin/SecureActivity.java new file mode 100644 index 000000000..d2ae34c80 --- /dev/null +++ b/samples/training/device-management-policy/src/com/example/training/deviceadmin/SecureActivity.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.training.deviceadmin; + +import android.app.Activity; +import android.content.Intent; + +public class SecureActivity extends Activity { + + @Override + protected void onResume() { + super.onResume(); + // Check to see if the device is properly secured as per the policy. Send user + // back to policy set up screen if necessary. + Policy policy = new Policy(this); + policy.readFromLocal(); + if (!policy.isDeviceSecured()) { + Intent intent = new Intent(); + intent.setClass(this, PolicySetupActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | + Intent.FLAG_ACTIVITY_CLEAR_TASK); + startActivity(intent); + finish(); + } + setContentView(R.layout.activity_secure); + } +} + |
