SecurityPolicy.java revision 345fb8b737c1632fb2a7e69ac44b8612be6237ed
1/* 2 * Copyright (C) 2010 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 17package com.android.email; 18 19import com.android.email.provider.EmailContent.Account; 20 21import android.app.DeviceAdmin; 22import android.content.Context; 23import android.content.Intent; 24import android.database.Cursor; 25 26/** 27 * Utility functions to support reading and writing security policies 28 */ 29public class SecurityPolicy { 30 31 private static SecurityPolicy sInstance = null; 32 private Context mContext; 33 34 /** 35 * This projection on Account is for scanning/reading 36 */ 37 private static final String[] ACCOUNT_SECURITY_PROJECTION = new String[] { 38 Account.RECORD_ID, Account.SECURITY_FLAGS 39 }; 40 private static final int ACCOUNT_SECURITY_COLUMN_FLAGS = 1; 41 // Note, this handles the NULL case to deal with older accounts where the column was added 42 private static final String WHERE_ACCOUNT_SECURITY_NONZERO = 43 Account.SECURITY_FLAGS + " IS NOT NULL AND " + Account.SECURITY_FLAGS + "!=0"; 44 45 /** 46 * Get the security policy instance 47 */ 48 public synchronized static SecurityPolicy getInstance(Context context) { 49 if (sInstance == null) { 50 sInstance = new SecurityPolicy(context); 51 } 52 return sInstance; 53 } 54 55 /** 56 * Private constructor (one time only) 57 */ 58 private SecurityPolicy(Context context) { 59 mContext = context; 60 } 61 62 /** 63 * For testing only: Inject context into already-created instance 64 */ 65 /* package */ void setContext(Context context) { 66 mContext = context; 67 } 68 69 /** 70 * Compute the aggregate policy for all accounts that require it, and record it. 71 * 72 * The business logic is as follows: 73 * min password length take the max 74 * password mode take the max (strongest mode) 75 * max password fails take the min 76 * max screen lock time take the min 77 * require remote wipe take the max (logical or) 78 * 79 * @return a policy representing the strongest aggregate, or null if none are defined 80 */ 81 /* package */ PolicySet computeAggregatePolicy() { 82 boolean policiesFound = false; 83 84 int minPasswordLength = Integer.MIN_VALUE; 85 int passwordMode = Integer.MIN_VALUE; 86 int maxPasswordFails = Integer.MAX_VALUE; 87 int maxScreenLockTime = Integer.MAX_VALUE; 88 boolean requireRemoteWipe = false; 89 90 Cursor c = mContext.getContentResolver().query(Account.CONTENT_URI, 91 ACCOUNT_SECURITY_PROJECTION, WHERE_ACCOUNT_SECURITY_NONZERO, null, null); 92 try { 93 while (c.moveToNext()) { 94 int flags = c.getInt(ACCOUNT_SECURITY_COLUMN_FLAGS); 95 if (flags != 0) { 96 PolicySet p = new PolicySet(flags); 97 if (p.mMinPasswordLength > 0) { 98 minPasswordLength = Math.max(p.mMinPasswordLength, minPasswordLength); 99 } 100 if (p.mPasswordMode > 0) { 101 passwordMode = Math.max(p.mPasswordMode, passwordMode); 102 } 103 if (p.mMaxPasswordFails > 0) { 104 maxPasswordFails = Math.min(p.mMaxPasswordFails, maxPasswordFails); 105 } 106 if (p.mMaxScreenLockTime > 0) { 107 maxScreenLockTime = Math.min(p.mMaxScreenLockTime, maxScreenLockTime); 108 } 109 requireRemoteWipe |= p.mRequireRemoteWipe; 110 policiesFound = true; 111 } 112 } 113 } finally { 114 c.close(); 115 } 116 if (policiesFound) { 117 return new PolicySet(minPasswordLength, passwordMode, maxPasswordFails, 118 maxScreenLockTime, requireRemoteWipe); 119 } else { 120 return null; 121 } 122 } 123 124 /** 125 * Query used to determine if a given policy is "possible" (irrespective of current 126 * device state. This is used when creating new accounts. 127 * 128 * TO BE IMPLEMENTED 129 * 130 * @param policies the policies requested 131 * @return true if the policies are supported, false if not supported 132 */ 133 public boolean isSupported(PolicySet policies) { 134 return true; 135 } 136 137 /** 138 * Query used to determine if a given policy is "active" (the device is operating at 139 * the required security level). This is used when creating new accounts. 140 * 141 * TO BE IMPLEMENTED 142 * 143 * @param policies the policies requested 144 * @return true if the policies are active, false if not active 145 */ 146 public boolean isActive(PolicySet policies) { 147 return true; 148 } 149 150 /** 151 * Sync service should call this any time a sync fails due to isActive() returning false. 152 * This will kick off the acquire-admin-state process and/or increase the security level. 153 * The caller needs to write the required policies into this account before making this call. 154 * 155 * @param accountId the account for which sync cannot proceed 156 */ 157 public void policiesRequired(long accountId) { 158 // implement.... 159 } 160 161 /** 162 * Class for tracking policies and reading/writing into accounts 163 */ 164 public static class PolicySet { 165 166 // Security (provisioning) flags 167 // bits 0..4: password length (0=no password required) 168 private static final int PASSWORD_LENGTH_MASK = 31; 169 private static final int PASSWORD_LENGTH_SHIFT = 0; 170 public static final int PASSWORD_LENGTH_MAX = 31; 171 // bits 5..8: password mode 172 private static final int PASSWORD_MODE_SHIFT = 5; 173 private static final int PASSWORD_MODE_MASK = 15 << PASSWORD_MODE_SHIFT; 174 public static final int PASSWORD_MODE_NONE = 0 << PASSWORD_MODE_SHIFT; 175 public static final int PASSWORD_MODE_SIMPLE = 1 << PASSWORD_MODE_SHIFT; 176 public static final int PASSWORD_MODE_STRONG = 2 << PASSWORD_MODE_SHIFT; 177 // bits 9..13: password failures -> wipe device (0=disabled) 178 private static final int PASSWORD_MAX_FAILS_SHIFT = 9; 179 private static final int PASSWORD_MAX_FAILS_MASK = 31 << PASSWORD_MAX_FAILS_SHIFT; 180 public static final int PASSWORD_MAX_FAILS_MAX = 31; 181 // bits 14..24: seconds to screen lock (0=not required) 182 private static final int SCREEN_LOCK_TIME_SHIFT = 14; 183 private static final int SCREEN_LOCK_TIME_MASK = 2047 << SCREEN_LOCK_TIME_SHIFT; 184 public static final int SCREEN_LOCK_TIME_MAX = 2047; 185 // bit 25: remote wipe capability required 186 private static final int REQUIRE_REMOTE_WIPE = 1 << 25; 187 188 public final int mMinPasswordLength; 189 public final int mPasswordMode; 190 public final int mMaxPasswordFails; 191 public final int mMaxScreenLockTime; 192 public final boolean mRequireRemoteWipe; 193 194 /** 195 * Create from raw values. 196 * @param minPasswordLength (0=not enforced) 197 * @param passwordMode 198 * @param maxPasswordFails (0=not enforced) 199 * @param maxScreenLockTime (0=not enforced) 200 * @param requireRemoteWipe 201 */ 202 public PolicySet(int minPasswordLength, int passwordMode, int maxPasswordFails, 203 int maxScreenLockTime, boolean requireRemoteWipe) { 204 if (minPasswordLength > PASSWORD_LENGTH_MAX) { 205 throw new IllegalArgumentException("password length"); 206 } 207 if (passwordMode < PASSWORD_MODE_NONE 208 || passwordMode > PASSWORD_MODE_STRONG) { 209 throw new IllegalArgumentException("password mode"); 210 } 211 if (maxPasswordFails > PASSWORD_MAX_FAILS_MAX) { 212 throw new IllegalArgumentException("password max fails"); 213 } 214 if (maxScreenLockTime > SCREEN_LOCK_TIME_MAX) { 215 throw new IllegalArgumentException("max screen lock time"); 216 } 217 218 mMinPasswordLength = minPasswordLength; 219 mPasswordMode = passwordMode; 220 mMaxPasswordFails = maxPasswordFails; 221 mMaxScreenLockTime = maxScreenLockTime; 222 mRequireRemoteWipe = requireRemoteWipe; 223 } 224 225 /** 226 * Create from values encoded in an account 227 * @param account 228 */ 229 public PolicySet(Account account) { 230 this(account.mSecurityFlags); 231 } 232 233 /** 234 * Create from values encoded in an account flags int 235 */ 236 public PolicySet(int flags) { 237 mMinPasswordLength = 238 (flags & PASSWORD_LENGTH_MASK) >> PASSWORD_LENGTH_SHIFT; 239 mPasswordMode = 240 (flags & PASSWORD_MODE_MASK); 241 mMaxPasswordFails = 242 (flags & PASSWORD_MAX_FAILS_MASK) >> PASSWORD_MAX_FAILS_SHIFT; 243 mMaxScreenLockTime = 244 (flags & SCREEN_LOCK_TIME_MASK) >> SCREEN_LOCK_TIME_SHIFT; 245 mRequireRemoteWipe = 0 != (flags & REQUIRE_REMOTE_WIPE); 246 } 247 248 /** 249 * Record flags (and a sync key for the flags) into an Account 250 * Note: the hash code is defined as the encoding used in Account 251 * @param account to write the values mSecurityFlags and mSecuritySyncKey 252 * @param syncKey the value to write into the account's mSecuritySyncKey 253 */ 254 public void writeAccount(Account account, String syncKey) { 255 account.mSecurityFlags = hashCode(); 256 } 257 258 @Override 259 public boolean equals(Object o) { 260 if (o instanceof PolicySet) { 261 PolicySet other = (PolicySet)o; 262 return (this.mMinPasswordLength == other.mMinPasswordLength) 263 && (this.mPasswordMode == other.mPasswordMode) 264 && (this.mMaxPasswordFails == other.mMaxPasswordFails) 265 && (this.mMaxScreenLockTime == other.mMaxScreenLockTime) 266 && (this.mRequireRemoteWipe == other.mRequireRemoteWipe); 267 } 268 return false; 269 } 270 271 /** 272 * Note: the hash code is defined as the encoding used in Account 273 */ 274 @Override 275 public int hashCode() { 276 int flags = 0; 277 flags = mMinPasswordLength << PASSWORD_LENGTH_SHIFT; 278 flags |= mPasswordMode; 279 flags |= mMaxPasswordFails << PASSWORD_MAX_FAILS_SHIFT; 280 flags |= mMaxScreenLockTime << SCREEN_LOCK_TIME_SHIFT; 281 if (mRequireRemoteWipe) { 282 flags |= REQUIRE_REMOTE_WIPE; 283 } 284 return flags; 285 } 286 287 @Override 288 public String toString() { 289 return "{ " + "pw-len-min=" + mMinPasswordLength + " pw-mode=" + mPasswordMode 290 + " pw-fails-max=" + mMaxPasswordFails + " screenlock-max=" 291 + mMaxScreenLockTime + " remote-wipe-req=" + mRequireRemoteWipe + "}"; 292 } 293 } 294 295 /** 296 * Device Policy administrator. This is primarily a listener for device state changes. 297 */ 298 private static class PolicyAdmin extends DeviceAdmin { 299 300 boolean mEnabled = false; 301 302 /** 303 * Called after the administrator is first enabled. 304 */ 305 @Override 306 public void onEnabled(Context context, Intent intent) { 307 mEnabled = true; 308 // do something 309 } 310 311 /** 312 * Called prior to the administrator being disabled. 313 */ 314 @Override 315 public void onDisabled(Context context, Intent intent) { 316 mEnabled = false; 317 // do something 318 } 319 320 /** 321 * Called after the user has changed their password. 322 */ 323 @Override 324 public void onPasswordChanged(Context context, Intent intent) { 325 // do something 326 } 327 328 /** 329 * Called after the user has failed at entering their current password. 330 */ 331 @Override 332 public void onPasswordFailed(Context context, Intent intent) { 333 // do something 334 } 335 } 336} 337