diff options
Diffstat (limited to 'core/java/com')
14 files changed, 663 insertions, 710 deletions
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 649a59f..144cc33 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -841,6 +841,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic Log.d(TAG, "Error calling setLastChosenActivity\n" + re); } + // Clear the value of mOtherProfile from previous call. + mOtherProfile = null; mList.clear(); if (mBaseResolveList != null) { currentResolveList = mOrigResolveList = mBaseResolveList; diff --git a/core/java/com/android/internal/util/UserIcons.java b/core/java/com/android/internal/util/UserIcons.java index e1e9d5e..c69d14f 100644 --- a/core/java/com/android/internal/util/UserIcons.java +++ b/core/java/com/android/internal/util/UserIcons.java @@ -48,9 +48,12 @@ public class UserIcons { if (icon == null) { return null; } - Bitmap bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(), icon.getIntrinsicHeight(), - Bitmap.Config.ARGB_8888); - icon.draw(new Canvas(bitmap)); + final int width = icon.getIntrinsicWidth(); + final int height = icon.getIntrinsicHeight(); + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + icon.setBounds(0, 0, width, height); + icon.draw(canvas); return bitmap; } diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java index 7eec392..f75b139 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java +++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java @@ -217,14 +217,14 @@ public class ActionMenuItemView extends TextView } @Override - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { onPopulateAccessibilityEvent(event); return true; } @Override - public void onPopulateAccessibilityEvent(AccessibilityEvent event) { - super.onPopulateAccessibilityEvent(event); + public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) { + super.onPopulateAccessibilityEventInternal(event); final CharSequence cdesc = getContentDescription(); if (!TextUtils.isEmpty(cdesc)) { event.getText().add(cdesc); diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java index 692bdac..29ac3f3 100644 --- a/core/java/com/android/internal/view/menu/ListMenuItemView.java +++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java @@ -276,8 +276,8 @@ public class ListMenuItemView extends LinearLayout implements MenuView.ItemView } @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(info); + public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfoInternal(info); if (mItemData != null && mItemData.hasSubMenu()) { info.setCanOpenPopup(true); diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java index 99bb1ac..2b20b38 100644 --- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java +++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java @@ -118,6 +118,10 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On mDropDownGravity = gravity; } + public int getGravity() { + return mDropDownGravity; + } + public void show() { if (!tryShow()) { throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor"); @@ -135,7 +139,7 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On mPopup.setAdapter(mAdapter); mPopup.setModal(true); - View anchor = mAnchorView; + final View anchor = mAnchorView; if (anchor != null) { final boolean addGlobalListener = mTreeObserver == null; mTreeObserver = anchor.getViewTreeObserver(); // Refresh to latest diff --git a/core/java/com/android/internal/widget/AccessibleDateAnimator.java b/core/java/com/android/internal/widget/AccessibleDateAnimator.java index e91a55c..f97a5d1 100644 --- a/core/java/com/android/internal/widget/AccessibleDateAnimator.java +++ b/core/java/com/android/internal/widget/AccessibleDateAnimator.java @@ -40,7 +40,7 @@ public class AccessibleDateAnimator extends ViewAnimator { * Announce the currently-selected date when launched. */ @Override - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { // Clear the event's current text so that only the current date will be spoken. event.getText().clear(); @@ -51,6 +51,6 @@ public class AccessibleDateAnimator extends ViewAnimator { event.getText().add(dateString); return true; } - return super.dispatchPopulateAccessibilityEvent(event); + return super.dispatchPopulateAccessibilityEventInternal(event); } } diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java index 7c671e8..5d3f464 100644 --- a/core/java/com/android/internal/widget/ActionBarContextView.java +++ b/core/java/com/android/internal/widget/ActionBarContextView.java @@ -529,7 +529,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi } @Override - public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) { if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { // Action mode started event.setSource(this); @@ -537,7 +537,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi event.setPackageName(getContext().getPackageName()); event.setContentDescription(mTitle); } else { - super.onInitializeAccessibilityEvent(event); + super.onInitializeAccessibilityEventInternal(event); } } diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 654d08b..88436f8 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -1463,14 +1463,14 @@ public class ActionBarView extends AbsActionBarView implements DecorToolbar { } @Override - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { onPopulateAccessibilityEvent(event); return true; } @Override - public void onPopulateAccessibilityEvent(AccessibilityEvent event) { - super.onPopulateAccessibilityEvent(event); + public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) { + super.onPopulateAccessibilityEventInternal(event); final CharSequence cdesc = getContentDescription(); if (!TextUtils.isEmpty(cdesc)) { event.getText().add(cdesc); diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index 9501f92..0cb1f38 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -31,5 +31,4 @@ interface ILockSettings { boolean checkVoldPassword(int userId); boolean havePattern(int userId); boolean havePassword(int userId); - void removeUser(int userId); } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 0afc651..ec01703 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -21,11 +21,9 @@ import android.app.ActivityManagerNative; import android.app.AlarmManager; import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; -import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; -import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.os.AsyncTask; @@ -42,13 +40,12 @@ import android.provider.Settings; import android.telecom.TelecomManager; import android.text.TextUtils; import android.util.Log; -import android.view.IWindowManager; import android.view.View; import android.widget.Button; import com.android.internal.R; import com.google.android.collect.Lists; -import java.io.ByteArrayOutputStream; + import java.nio.charset.StandardCharsets; import libcore.util.HexEncoding; @@ -109,46 +106,25 @@ public class LockPatternUtils { */ public static final int MIN_PATTERN_REGISTER_FAIL = MIN_LOCK_PATTERN_SIZE; - /** - * Tells the keyguard to show the user switcher when the keyguard is created. - */ - public static final String KEYGUARD_SHOW_USER_SWITCHER = "showuserswitcher"; - - /** - * Tells the keyguard to show the security challenge when the keyguard is created. - */ - public static final String KEYGUARD_SHOW_SECURITY_CHALLENGE = "showsecuritychallenge"; - - /** - * Tells the keyguard to show the widget with the specified id when the keyguard is created. - */ - public static final String KEYGUARD_SHOW_APPWIDGET = "showappwidget"; - - /** - * The bit in LOCK_BIOMETRIC_WEAK_FLAGS to be used to indicate whether liveliness should - * be used - */ - public static final int FLAG_BIOMETRIC_WEAK_LIVELINESS = 0x1; - - /** - * Pseudo-appwidget id we use to represent the default clock status widget - */ - public static final int ID_DEFAULT_STATUS_WIDGET = -2; - + @Deprecated public final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently"; public final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline"; public final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen"; public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type"; - public static final String PASSWORD_TYPE_ALTERNATE_KEY = "lockscreen.password_type_alternate"; + @Deprecated + public final static String PASSWORD_TYPE_ALTERNATE_KEY = "lockscreen.password_type_alternate"; public final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt"; public final static String DISABLE_LOCKSCREEN_KEY = "lockscreen.disabled"; public final static String LOCKSCREEN_OPTIONS = "lockscreen.options"; + @Deprecated public final static String LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK = "lockscreen.biometric_weak_fallback"; + @Deprecated public final static String BIOMETRIC_WEAK_EVER_CHOSEN_KEY = "lockscreen.biometricweakeverchosen"; public final static String LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS = "lockscreen.power_button_instantly_locks"; + @Deprecated public final static String LOCKSCREEN_WIDGETS_ENABLED = "lockscreen.widgets_enabled"; public final static String PASSWORD_HISTORY_KEY = "lockscreen.passwordhistory"; @@ -227,7 +203,11 @@ public class LockPatternUtils { } public int getRequestedPasswordHistoryLength() { - return getDevicePolicyManager().getPasswordHistoryLength(null, getCurrentOrCallingUserId()); + return getRequestedPasswordHistoryLength(getCurrentOrCallingUserId()); + } + + private int getRequestedPasswordHistoryLength(int userId) { + return getDevicePolicyManager().getPasswordHistoryLength(null, userId); } public int getRequestedPasswordMinimumLetters() { @@ -289,14 +269,6 @@ public class LockPatternUtils { } } - public void removeUser(int userId) { - try { - getLockSettings().removeUser(userId); - } catch (RemoteException re) { - Log.e(TAG, "Couldn't remove lock settings for user " + userId); - } - } - private int getCurrentOrCallingUserId() { if (mMultiUserMode) { // TODO: This is a little inefficient. See if all users of this are able to @@ -359,9 +331,10 @@ public class LockPatternUtils { * @return Whether the password matches any in the history. */ public boolean checkPasswordHistory(String password) { + int userId = getCurrentOrCallingUserId(); String passwordHashString = new String( - passwordToHash(password, getCurrentOrCallingUserId()), StandardCharsets.UTF_8); - String passwordHistory = getString(PASSWORD_HISTORY_KEY); + passwordToHash(password, userId), StandardCharsets.UTF_8); + String passwordHistory = getString(PASSWORD_HISTORY_KEY, userId); if (passwordHistory == null) { return false; } @@ -383,15 +356,7 @@ public class LockPatternUtils { * Check to see if the user has stored a lock pattern. * @return Whether a saved pattern exists. */ - public boolean savedPatternExists() { - return savedPatternExists(getCurrentOrCallingUserId()); - } - - /** - * Check to see if the user has stored a lock pattern. - * @return Whether a saved pattern exists. - */ - public boolean savedPatternExists(int userId) { + private boolean savedPatternExists(int userId) { try { return getLockSettings().havePattern(userId); } catch (RemoteException re) { @@ -403,15 +368,7 @@ public class LockPatternUtils { * Check to see if the user has stored a lock pattern. * @return Whether a saved pattern exists. */ - public boolean savedPasswordExists() { - return savedPasswordExists(getCurrentOrCallingUserId()); - } - - /** - * Check to see if the user has stored a lock pattern. - * @return Whether a saved pattern exists. - */ - public boolean savedPasswordExists(int userId) { + private boolean savedPasswordExists(int userId) { try { return getLockSettings().havePassword(userId); } catch (RemoteException re) { @@ -426,86 +383,62 @@ public class LockPatternUtils { * @return True if the user has ever chosen a pattern. */ public boolean isPatternEverChosen() { - return getBoolean(PATTERN_EVER_CHOSEN_KEY, false); + return getBoolean(PATTERN_EVER_CHOSEN_KEY, false, getCurrentOrCallingUserId()); } /** - * Return true if the user has ever chosen biometric weak. This is true even if biometric - * weak is not current set. - * - * @return True if the user has ever chosen biometric weak. + * Used by device policy manager to validate the current password + * information it has. */ - public boolean isBiometricWeakEverChosen() { - return getBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY, false); + public int getActivePasswordQuality() { + return getActivePasswordQuality(getCurrentOrCallingUserId()); } /** * Used by device policy manager to validate the current password * information it has. */ - public int getActivePasswordQuality() { - int activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; - // Note we don't want to use getKeyguardStoredPasswordQuality() because we want this to - // return biometric_weak if that is being used instead of the backup - int quality = - (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); - switch (quality) { - case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: - if (isLockPatternEnabled()) { - activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; - } - break; - case DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK: - if (isBiometricWeakInstalled()) { - activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK; - } - break; - case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: - if (isLockPasswordEnabled()) { - activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; - } - break; - case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX: - if (isLockPasswordEnabled()) { - activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX; - } - break; - case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: - if (isLockPasswordEnabled()) { - activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; - } - break; - case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: - if (isLockPasswordEnabled()) { - activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; - } - break; - case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: - if (isLockPasswordEnabled()) { - activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; - } - break; + public int getActivePasswordQuality(int userId) { + int quality = getKeyguardStoredPasswordQuality(userId); + + if (isLockPasswordEnabled(quality, userId)) { + // Quality is a password and a password exists. Return the quality. + return quality; + } + + if (isLockPatternEnabled(quality, userId)) { + // Quality is a pattern and a pattern exists. Return the quality. + return quality; } - return activePasswordQuality; + return DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; } - public void clearLock(boolean isFallback) { - clearLock(isFallback, getCurrentOrCallingUserId()); + public void clearLock() { + clearLock(getCurrentOrCallingUserId()); } /** * Clear any lock pattern or password. */ - public void clearLock(boolean isFallback, int userHandle) { - if(!isFallback) deleteGallery(userHandle); - saveLockPassword(null, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, isFallback, - userHandle); - setLockPatternEnabled(false, userHandle); - saveLockPattern(null, isFallback, userHandle); + public void clearLock(int userHandle) { setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle); - setLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, - userHandle); + + try { + getLockSettings().setLockPassword(null, userHandle); + getLockSettings().setLockPattern(null, userHandle); + } catch (RemoteException e) { + // well, we tried... + } + + if (userHandle == UserHandle.USER_OWNER) { + // Set the encryption password to default. + updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null); + } + + getDevicePolicyManager().setActivePasswordState( + DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0, userHandle); + onAfterChangingPassword(userHandle); } @@ -516,7 +449,7 @@ public class LockPatternUtils { * @param disable Disables lock screen when true */ public void setLockScreenDisabled(boolean disable) { - setLong(DISABLE_LOCKSCREEN_KEY, disable ? 1 : 0); + setBoolean(DISABLE_LOCKSCREEN_KEY, disable, getCurrentOrCallingUserId()); } /** @@ -526,7 +459,7 @@ public class LockPatternUtils { * @return true if lock screen is can be disabled */ public boolean isLockScreenDisabled() { - if (!isSecure() && getLong(DISABLE_LOCKSCREEN_KEY, 0) != 0) { + if (!isSecure() && getBoolean(DISABLE_LOCKSCREEN_KEY, false, getCurrentOrCallingUserId())) { // Check if the number of switchable users forces the lockscreen. final List<UserInfo> users = UserManager.get(mContext).getUsers(true); final int userCount = users.size(); @@ -542,96 +475,56 @@ public class LockPatternUtils { } /** - * Calls back SetupFaceLock to delete the temporary gallery file - */ - public void deleteTempGallery() { - Intent intent = new Intent().setAction("com.android.facelock.DELETE_GALLERY"); - intent.putExtra("deleteTempGallery", true); - mContext.sendBroadcast(intent); - } - - /** - * Calls back SetupFaceLock to delete the gallery file when the lock type is changed - */ - void deleteGallery(int userId) { - if(usingBiometricWeak(userId)) { - Intent intent = new Intent().setAction("com.android.facelock.DELETE_GALLERY"); - intent.putExtra("deleteGallery", true); - mContext.sendBroadcastAsUser(intent, new UserHandle(userId)); - } - } - - /** * Save a lock pattern. * @param pattern The new pattern to save. */ public void saveLockPattern(List<LockPatternView.Cell> pattern) { - this.saveLockPattern(pattern, false); + this.saveLockPattern(pattern, getCurrentOrCallingUserId()); } /** * Save a lock pattern. * @param pattern The new pattern to save. - */ - public void saveLockPattern(List<LockPatternView.Cell> pattern, boolean isFallback) { - this.saveLockPattern(pattern, isFallback, getCurrentOrCallingUserId()); - } - - /** - * Save a lock pattern. - * @param pattern The new pattern to save. - * @param isFallback Specifies if this is a fallback to biometric weak * @param userId the user whose pattern is to be saved. */ - public void saveLockPattern(List<LockPatternView.Cell> pattern, boolean isFallback, - int userId) { + public void saveLockPattern(List<LockPatternView.Cell> pattern, int userId) { try { + if (pattern == null) { + throw new IllegalArgumentException("pattern must not be null"); + } + getLockSettings().setLockPattern(patternToString(pattern), userId); DevicePolicyManager dpm = getDevicePolicyManager(); - if (pattern != null) { - // Update the device encryption password. - if (userId == UserHandle.USER_OWNER - && LockPatternUtils.isDeviceEncryptionEnabled()) { - final boolean required = isCredentialRequiredToDecrypt(true); - if (!required) { - clearEncryptionPassword(); - } else { - String stringPattern = patternToString(pattern); - updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern); - } - } - setBoolean(PATTERN_EVER_CHOSEN_KEY, true, userId); - if (!isFallback) { - deleteGallery(userId); - setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId); - dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, - pattern.size(), 0, 0, 0, 0, 0, 0, userId); + // Update the device encryption password. + if (userId == UserHandle.USER_OWNER + && LockPatternUtils.isDeviceEncryptionEnabled()) { + final boolean required = isCredentialRequiredToDecrypt(true); + if (!required) { + clearEncryptionPassword(); } else { - setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK, userId); - setLong(PASSWORD_TYPE_ALTERNATE_KEY, - DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId); - finishBiometricWeak(userId); - dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK, - 0, 0, 0, 0, 0, 0, 0, userId); + String stringPattern = patternToString(pattern); + updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern); } - } else { - dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, - 0, 0, 0, 0, 0, userId); } + + setBoolean(PATTERN_EVER_CHOSEN_KEY, true, userId); + + setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId); + dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, + pattern.size(), 0, 0, 0, 0, 0, 0, userId); onAfterChangingPassword(userId); } catch (RemoteException re) { Log.e(TAG, "Couldn't save lock pattern " + re); } } - private void updateCryptoUserInfo() { - int userId = getCurrentOrCallingUserId(); + private void updateCryptoUserInfo(int userId) { if (userId != UserHandle.USER_OWNER) { return; } - final String ownerInfo = isOwnerInfoEnabled() ? getOwnerInfo(userId) : ""; + final String ownerInfo = isOwnerInfoEnabled(userId) ? getOwnerInfo(userId) : ""; IBinder service = ServiceManager.getService("mount"); if (service == null) { @@ -650,20 +543,25 @@ public class LockPatternUtils { public void setOwnerInfo(String info, int userId) { setString(LOCK_SCREEN_OWNER_INFO, info, userId); - updateCryptoUserInfo(); + updateCryptoUserInfo(userId); } public void setOwnerInfoEnabled(boolean enabled) { - setBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, enabled); - updateCryptoUserInfo(); + int userId = getCurrentOrCallingUserId(); + setBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, enabled, userId); + updateCryptoUserInfo(userId); } public String getOwnerInfo(int userId) { - return getString(LOCK_SCREEN_OWNER_INFO); + return getString(LOCK_SCREEN_OWNER_INFO, userId); } public boolean isOwnerInfoEnabled() { - return getBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, false); + return isOwnerInfoEnabled(getCurrentOrCallingUserId()); + } + + private boolean isOwnerInfoEnabled(int userId) { + return getBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, false, userId); } /** @@ -789,7 +687,7 @@ public class LockPatternUtils { * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)} */ public void saveLockPassword(String password, int quality) { - this.saveLockPassword(password, quality, false, getCurrentOrCallingUserId()); + saveLockPassword(password, quality, getCurrentOrCallingUserId()); } /** @@ -798,120 +696,87 @@ public class LockPatternUtils { * pattern. * @param password The password to save * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)} - * @param isFallback Specifies if this is a fallback to biometric weak - */ - public void saveLockPassword(String password, int quality, boolean isFallback) { - saveLockPassword(password, quality, isFallback, getCurrentOrCallingUserId()); - } - - /** - * Save a lock password. Does not ensure that the password is as good - * as the requested mode, but will adjust the mode to be as good as the - * pattern. - * @param password The password to save - * @param quality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)} - * @param isFallback Specifies if this is a fallback to biometric weak * @param userHandle The userId of the user to change the password for */ - public void saveLockPassword(String password, int quality, boolean isFallback, int userHandle) { + public void saveLockPassword(String password, int quality, int userHandle) { try { DevicePolicyManager dpm = getDevicePolicyManager(); - if (!TextUtils.isEmpty(password)) { - getLockSettings().setLockPassword(password, userHandle); - int computedQuality = computePasswordQuality(password); - - // Update the device encryption password. - if (userHandle == UserHandle.USER_OWNER - && LockPatternUtils.isDeviceEncryptionEnabled()) { - if (!isCredentialRequiredToDecrypt(true)) { - clearEncryptionPassword(); - } else { - boolean numeric = computedQuality - == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; - boolean numericComplex = computedQuality - == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX; - int type = numeric || numericComplex ? StorageManager.CRYPT_TYPE_PIN - : StorageManager.CRYPT_TYPE_PASSWORD; - updateEncryptionPassword(type, password); - } + if (TextUtils.isEmpty(password)) { + throw new IllegalArgumentException("password must not be null nor empty"); + } + + getLockSettings().setLockPassword(password, userHandle); + int computedQuality = computePasswordQuality(password); + + // Update the device encryption password. + if (userHandle == UserHandle.USER_OWNER + && LockPatternUtils.isDeviceEncryptionEnabled()) { + if (!isCredentialRequiredToDecrypt(true)) { + clearEncryptionPassword(); + } else { + boolean numeric = computedQuality + == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; + boolean numericComplex = computedQuality + == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX; + int type = numeric || numericComplex ? StorageManager.CRYPT_TYPE_PIN + : StorageManager.CRYPT_TYPE_PASSWORD; + updateEncryptionPassword(type, password); } + } - if (!isFallback) { - deleteGallery(userHandle); - setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle); - if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { - int letters = 0; - int uppercase = 0; - int lowercase = 0; - int numbers = 0; - int symbols = 0; - int nonletter = 0; - for (int i = 0; i < password.length(); i++) { - char c = password.charAt(i); - if (c >= 'A' && c <= 'Z') { - letters++; - uppercase++; - } else if (c >= 'a' && c <= 'z') { - letters++; - lowercase++; - } else if (c >= '0' && c <= '9') { - numbers++; - nonletter++; - } else { - symbols++; - nonletter++; - } - } - dpm.setActivePasswordState(Math.max(quality, computedQuality), - password.length(), letters, uppercase, lowercase, - numbers, symbols, nonletter, userHandle); + setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle); + if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { + int letters = 0; + int uppercase = 0; + int lowercase = 0; + int numbers = 0; + int symbols = 0; + int nonletter = 0; + for (int i = 0; i < password.length(); i++) { + char c = password.charAt(i); + if (c >= 'A' && c <= 'Z') { + letters++; + uppercase++; + } else if (c >= 'a' && c <= 'z') { + letters++; + lowercase++; + } else if (c >= '0' && c <= '9') { + numbers++; + nonletter++; } else { - // The password is not anything. - dpm.setActivePasswordState( - DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, - 0, 0, 0, 0, 0, 0, 0, userHandle); + symbols++; + nonletter++; } - } else { - // Case where it's a fallback for biometric weak - setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK, - userHandle); - setLong(PASSWORD_TYPE_ALTERNATE_KEY, Math.max(quality, computedQuality), - userHandle); - finishBiometricWeak(userHandle); - dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK, - 0, 0, 0, 0, 0, 0, 0, userHandle); } - // Add the password to the password history. We assume all - // password hashes have the same length for simplicity of implementation. - String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle); - if (passwordHistory == null) { - passwordHistory = ""; - } - int passwordHistoryLength = getRequestedPasswordHistoryLength(); - if (passwordHistoryLength == 0) { - passwordHistory = ""; - } else { - byte[] hash = passwordToHash(password, userHandle); - passwordHistory = new String(hash, StandardCharsets.UTF_8) + "," + passwordHistory; - // Cut it to contain passwordHistoryLength hashes - // and passwordHistoryLength -1 commas. - passwordHistory = passwordHistory.substring(0, Math.min(hash.length - * passwordHistoryLength + passwordHistoryLength - 1, passwordHistory - .length())); - } - setString(PASSWORD_HISTORY_KEY, passwordHistory, userHandle); + dpm.setActivePasswordState(Math.max(quality, computedQuality), + password.length(), letters, uppercase, lowercase, + numbers, symbols, nonletter, userHandle); } else { - // Empty password - getLockSettings().setLockPassword(null, userHandle); - if (userHandle == UserHandle.USER_OWNER) { - // Set the encryption password to default. - updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null); - } - + // The password is not anything. dpm.setActivePasswordState( - DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0, - userHandle); + DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, + 0, 0, 0, 0, 0, 0, 0, userHandle); + } + + // Add the password to the password history. We assume all + // password hashes have the same length for simplicity of implementation. + String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle); + if (passwordHistory == null) { + passwordHistory = ""; + } + int passwordHistoryLength = getRequestedPasswordHistoryLength(userHandle); + if (passwordHistoryLength == 0) { + passwordHistory = ""; + } else { + byte[] hash = passwordToHash(password, userHandle); + passwordHistory = new String(hash, StandardCharsets.UTF_8) + "," + passwordHistory; + // Cut it to contain passwordHistoryLength hashes + // and passwordHistoryLength -1 commas. + passwordHistory = passwordHistory.substring(0, Math.min(hash.length + * passwordHistoryLength + passwordHistoryLength - 1, passwordHistory + .length())); } + setString(PASSWORD_HISTORY_KEY, passwordHistory, userHandle); onAfterChangingPassword(userHandle); } catch (RemoteException re) { // Cant do much @@ -971,31 +836,8 @@ public class LockPatternUtils { * @return stored password quality */ public int getKeyguardStoredPasswordQuality(int userHandle) { - int quality = (int) getLong(PASSWORD_TYPE_KEY, + return (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle); - // If the user has chosen to use weak biometric sensor, then return the backup locking - // method and treat biometric as a special case. - if (quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK) { - quality = (int) getLong(PASSWORD_TYPE_ALTERNATE_KEY, - DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle); - } - return quality; - } - - /** - * @return true if the lockscreen method is set to biometric weak - */ - public boolean usingBiometricWeak() { - return usingBiometricWeak(getCurrentOrCallingUserId()); - } - - /** - * @return true if the lockscreen method is set to biometric weak - */ - public boolean usingBiometricWeak(int userId) { - int quality = (int) getLong( - PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId); - return quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK; } /** @@ -1004,6 +846,10 @@ public class LockPatternUtils { * @return The pattern. */ public static List<LockPatternView.Cell> stringToPattern(String string) { + if (string == null) { + return null; + } + List<LockPatternView.Cell> result = Lists.newArrayList(); final byte[] bytes = string.getBytes(); @@ -1106,126 +952,73 @@ public class LockPatternUtils { } /** - * @return Whether the lock password is enabled, or if it is set as a backup for biometric weak + * @return Whether the lock screen is secured. */ - public boolean isLockPasswordEnabled() { - long mode = getLong(PASSWORD_TYPE_KEY, 0); - long backupMode = getLong(PASSWORD_TYPE_ALTERNATE_KEY, 0); - final boolean passwordEnabled = mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC - || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC - || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX - || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC - || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; - final boolean backupEnabled = backupMode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC - || backupMode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC - || backupMode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX - || backupMode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC - || backupMode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; - - return savedPasswordExists() && (passwordEnabled || - (usingBiometricWeak() && backupEnabled)); + public boolean isSecure() { + return isSecure(getCurrentOrCallingUserId()); } /** - * @return Whether the lock pattern is enabled, or if it is set as a backup for biometric weak + * @param userId the user for which to report the value + * @return Whether the lock screen is secured. */ - public boolean isLockPatternEnabled() { - return isLockPatternEnabled(getCurrentOrCallingUserId()); + public boolean isSecure(int userId) { + int mode = getKeyguardStoredPasswordQuality(userId); + return isLockPatternEnabled(mode, userId) || isLockPasswordEnabled(mode, userId); } /** - * @return Whether the lock pattern is enabled, or if it is set as a backup for biometric weak + * @return Whether the lock password is enabled */ - public boolean isLockPatternEnabled(int userId) { - final boolean backupEnabled = - getLong(PASSWORD_TYPE_ALTERNATE_KEY, - DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId) - == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; - - return getBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, false, userId) - && (getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, - userId) == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING - || (usingBiometricWeak(userId) && backupEnabled)); + public boolean isLockPasswordEnabled() { + return isLockPasswordEnabled(getCurrentOrCallingUserId()); } - /** - * @return Whether biometric weak lock is installed and that the front facing camera exists - */ - public boolean isBiometricWeakInstalled() { - // Check that it's installed - PackageManager pm = mContext.getPackageManager(); - try { - pm.getPackageInfo("com.android.facelock", PackageManager.GET_ACTIVITIES); - } catch (PackageManager.NameNotFoundException e) { - return false; - } - - // Check that the camera is enabled - if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) { - return false; - } - if (getDevicePolicyManager().getCameraDisabled(null, getCurrentOrCallingUserId())) { - return false; - } - - // TODO: If we decide not to proceed with Face Unlock as a trustlet, this must be changed - // back to returning true. If we become certain that Face Unlock will be a trustlet, this - // entire function and a lot of other code can be removed. - if (DEBUG) Log.d(TAG, "Forcing isBiometricWeakInstalled() to return false to disable it"); - return false; + public boolean isLockPasswordEnabled(int userId) { + return isLockPasswordEnabled(getKeyguardStoredPasswordQuality(userId), userId); } - /** - * Set whether biometric weak liveliness is enabled. - */ - public void setBiometricWeakLivelinessEnabled(boolean enabled) { - long currentFlag = getLong(Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS, 0L); - long newFlag; - if (enabled) { - newFlag = currentFlag | FLAG_BIOMETRIC_WEAK_LIVELINESS; - } else { - newFlag = currentFlag & ~FLAG_BIOMETRIC_WEAK_LIVELINESS; - } - setLong(Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS, newFlag); + private boolean isLockPasswordEnabled(int mode, int userId) { + final boolean passwordEnabled = mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC + || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC + || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX + || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC + || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; + return passwordEnabled && savedPasswordExists(userId); } /** - * @return Whether the biometric weak liveliness is enabled. + * @return Whether the lock pattern is enabled */ - public boolean isBiometricWeakLivelinessEnabled() { - long currentFlag = getLong(Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS, 0L); - return ((currentFlag & FLAG_BIOMETRIC_WEAK_LIVELINESS) != 0); + public boolean isLockPatternEnabled() { + return isLockPatternEnabled(getCurrentOrCallingUserId()); } - /** - * Set whether the lock pattern is enabled. - */ - public void setLockPatternEnabled(boolean enabled) { - setLockPatternEnabled(enabled, getCurrentOrCallingUserId()); + public boolean isLockPatternEnabled(int userId) { + return isLockPatternEnabled(getKeyguardStoredPasswordQuality(userId), userId); } - /** - * Set whether the lock pattern is enabled. - */ - public void setLockPatternEnabled(boolean enabled, int userHandle) { - setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, enabled, userHandle); + private boolean isLockPatternEnabled(int mode, int userId) { + return mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING + && savedPatternExists(userId); } /** * @return Whether the visible pattern is enabled. */ public boolean isVisiblePatternEnabled() { - return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, false); + return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, false, getCurrentOrCallingUserId()); } /** * Set whether the visible pattern is enabled. */ public void setVisiblePatternEnabled(boolean enabled) { - setBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, enabled); + int userId = getCurrentOrCallingUserId(); + + setBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, enabled, userId); // Update for crypto if owner - int userId = getCurrentOrCallingUserId(); if (userId != UserHandle.USER_OWNER) { return; } @@ -1259,7 +1052,7 @@ public class LockPatternUtils { */ public long setLockoutAttemptDeadline() { final long deadline = SystemClock.elapsedRealtime() + FAILED_ATTEMPT_TIMEOUT_MS; - setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline); + setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline, getCurrentOrCallingUserId()); return deadline; } @@ -1269,7 +1062,7 @@ public class LockPatternUtils { * enter a pattern. */ public long getLockoutAttemptDeadline() { - final long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L); + final long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L, getCurrentOrCallingUserId()); final long now = SystemClock.elapsedRealtime(); if (deadline < now || deadline > (now + FAILED_ATTEMPT_TIMEOUT_MS)) { return 0L; @@ -1277,27 +1070,6 @@ public class LockPatternUtils { return deadline; } - /** - * @return Whether the user is permanently locked out until they verify their - * credentials. Occurs after {@link #FAILED_ATTEMPTS_BEFORE_RESET} failed - * attempts. - */ - public boolean isPermanentlyLocked() { - return getBoolean(LOCKOUT_PERMANENT_KEY, false); - } - - /** - * Set the state of whether the device is permanently locked, meaning the user - * must authenticate via other means. - * - * @param locked Whether the user is permanently locked out until they verify their - * credentials. Occurs after {@link #FAILED_ATTEMPTS_BEFORE_RESET} failed - * attempts. - */ - public void setPermanentlyLocked(boolean locked) { - setBoolean(LOCKOUT_PERMANENT_KEY, locked); - } - public boolean isEmergencyCallCapable() { return mContext.getResources().getBoolean( com.android.internal.R.bool.config_voice_capable); @@ -1313,15 +1085,6 @@ public class LockPatternUtils { com.android.internal.R.bool.config_enable_emergency_call_while_sim_locked); } - /** - * @return A formatted string of the next alarm (for showing on the lock screen), - * or null if there is no next alarm. - */ - public AlarmManager.AlarmClockInfo getNextAlarm() { - AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); - return alarmManager.getNextAlarmClock(UserHandle.USER_CURRENT); - } - private boolean getBoolean(String secureSettingKey, boolean defaultValue, int userId) { try { return getLockSettings().getBoolean(secureSettingKey, defaultValue, userId); @@ -1330,10 +1093,6 @@ public class LockPatternUtils { } } - private boolean getBoolean(String secureSettingKey, boolean defaultValue) { - return getBoolean(secureSettingKey, defaultValue, getCurrentOrCallingUserId()); - } - private void setBoolean(String secureSettingKey, boolean enabled, int userId) { try { getLockSettings().setBoolean(secureSettingKey, enabled, userId); @@ -1343,144 +1102,6 @@ public class LockPatternUtils { } } - private void setBoolean(String secureSettingKey, boolean enabled) { - setBoolean(secureSettingKey, enabled, getCurrentOrCallingUserId()); - } - - public int[] getAppWidgets() { - return getAppWidgets(UserHandle.USER_CURRENT); - } - - private int[] getAppWidgets(int userId) { - String appWidgetIdString = Settings.Secure.getStringForUser( - mContentResolver, Settings.Secure.LOCK_SCREEN_APPWIDGET_IDS, userId); - String delims = ","; - if (appWidgetIdString != null && appWidgetIdString.length() > 0) { - String[] appWidgetStringIds = appWidgetIdString.split(delims); - int[] appWidgetIds = new int[appWidgetStringIds.length]; - for (int i = 0; i < appWidgetStringIds.length; i++) { - String appWidget = appWidgetStringIds[i]; - try { - appWidgetIds[i] = Integer.decode(appWidget); - } catch (NumberFormatException e) { - Log.d(TAG, "Error when parsing widget id " + appWidget); - return null; - } - } - return appWidgetIds; - } - return new int[0]; - } - - private static String combineStrings(int[] list, String separator) { - int listLength = list.length; - - switch (listLength) { - case 0: { - return ""; - } - case 1: { - return Integer.toString(list[0]); - } - } - - int strLength = 0; - int separatorLength = separator.length(); - - String[] stringList = new String[list.length]; - for (int i = 0; i < listLength; i++) { - stringList[i] = Integer.toString(list[i]); - strLength += stringList[i].length(); - if (i < listLength - 1) { - strLength += separatorLength; - } - } - - StringBuilder sb = new StringBuilder(strLength); - - for (int i = 0; i < listLength; i++) { - sb.append(list[i]); - if (i < listLength - 1) { - sb.append(separator); - } - } - - return sb.toString(); - } - - // appwidget used when appwidgets are disabled (we make an exception for - // default clock widget) - public void writeFallbackAppWidgetId(int appWidgetId) { - Settings.Secure.putIntForUser(mContentResolver, - Settings.Secure.LOCK_SCREEN_FALLBACK_APPWIDGET_ID, - appWidgetId, - UserHandle.USER_CURRENT); - } - - // appwidget used when appwidgets are disabled (we make an exception for - // default clock widget) - public int getFallbackAppWidgetId() { - return Settings.Secure.getIntForUser( - mContentResolver, - Settings.Secure.LOCK_SCREEN_FALLBACK_APPWIDGET_ID, - AppWidgetManager.INVALID_APPWIDGET_ID, - UserHandle.USER_CURRENT); - } - - private void writeAppWidgets(int[] appWidgetIds) { - Settings.Secure.putStringForUser(mContentResolver, - Settings.Secure.LOCK_SCREEN_APPWIDGET_IDS, - combineStrings(appWidgetIds, ","), - UserHandle.USER_CURRENT); - } - - // TODO: log an error if this returns false - public boolean addAppWidget(int widgetId, int index) { - int[] widgets = getAppWidgets(); - if (widgets == null) { - return false; - } - if (index < 0 || index > widgets.length) { - return false; - } - int[] newWidgets = new int[widgets.length + 1]; - for (int i = 0, j = 0; i < newWidgets.length; i++) { - if (index == i) { - newWidgets[i] = widgetId; - i++; - } - if (i < newWidgets.length) { - newWidgets[i] = widgets[j]; - j++; - } - } - writeAppWidgets(newWidgets); - return true; - } - - public boolean removeAppWidget(int widgetId) { - int[] widgets = getAppWidgets(); - - if (widgets.length == 0) { - return false; - } - - int[] newWidgets = new int[widgets.length - 1]; - for (int i = 0, j = 0; i < widgets.length; i++) { - if (widgets[i] == widgetId) { - // continue... - } else if (j >= newWidgets.length) { - // we couldn't find the widget - return false; - } else { - newWidgets[j] = widgets[i]; - j++; - } - } - writeAppWidgets(newWidgets); - return true; - } - private long getLong(String secureSettingKey, long defaultValue, int userHandle) { try { return getLockSettings().getLong(secureSettingKey, defaultValue, userHandle); @@ -1489,19 +1110,6 @@ public class LockPatternUtils { } } - private long getLong(String secureSettingKey, long defaultValue) { - try { - return getLockSettings().getLong(secureSettingKey, defaultValue, - getCurrentOrCallingUserId()); - } catch (RemoteException re) { - return defaultValue; - } - } - - private void setLong(String secureSettingKey, long value) { - setLong(secureSettingKey, value, getCurrentOrCallingUserId()); - } - private void setLong(String secureSettingKey, long value, int userHandle) { try { getLockSettings().setLong(secureSettingKey, value, userHandle); @@ -1511,10 +1119,6 @@ public class LockPatternUtils { } } - private String getString(String secureSettingKey) { - return getString(secureSettingKey, getCurrentOrCallingUserId()); - } - private String getString(String secureSettingKey, int userHandle) { try { return getLockSettings().getString(secureSettingKey, null, userHandle); @@ -1532,24 +1136,6 @@ public class LockPatternUtils { } } - public boolean isSecure() { - return isSecure(getCurrentOrCallingUserId()); - } - - public boolean isSecure(int userId) { - long mode = getKeyguardStoredPasswordQuality(userId); - final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; - final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC - || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX - || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC - || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC - || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; - final boolean secure = - isPattern && isLockPatternEnabled(userId) && savedPatternExists(userId) - || isPassword && savedPasswordExists(userId); - return secure; - } - /** * Sets the emergency button visibility based on isEmergencyCallCapable(). * @@ -1602,64 +1188,13 @@ public class LockPatternUtils { return (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE); } - private void finishBiometricWeak(int userId) { - setBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY, true, userId); - - // Launch intent to show final screen, this also - // moves the temporary gallery to the actual gallery - Intent intent = new Intent(); - intent.setClassName("com.android.facelock", - "com.android.facelock.SetupEndScreen"); - mContext.startActivityAsUser(intent, new UserHandle(userId)); - } - public void setPowerButtonInstantlyLocks(boolean enabled) { - setBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, enabled); + setBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, enabled, getCurrentOrCallingUserId()); } public boolean getPowerButtonInstantlyLocks() { - return getBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, true); - } - - public static boolean isSafeModeEnabled() { - try { - return IWindowManager.Stub.asInterface( - ServiceManager.getService("window")).isSafeModeEnabled(); - } catch (RemoteException e) { - // Shouldn't happen! - } - return false; - } - - /** - * Determine whether the user has selected any non-system widgets in keyguard - * - * @return true if widgets have been selected - */ - public boolean hasWidgetsEnabledInKeyguard(int userid) { - int widgets[] = getAppWidgets(userid); - for (int i = 0; i < widgets.length; i++) { - if (widgets[i] > 0) { - return true; - } - } - return false; - } - - public boolean getWidgetsEnabled() { - return getWidgetsEnabled(getCurrentOrCallingUserId()); - } - - public boolean getWidgetsEnabled(int userId) { - return getBoolean(LOCKSCREEN_WIDGETS_ENABLED, false, userId); - } - - public void setWidgetsEnabled(boolean enabled) { - setWidgetsEnabled(enabled, getCurrentOrCallingUserId()); - } - - public void setWidgetsEnabled(boolean enabled, int userId) { - setBoolean(LOCKSCREEN_WIDGETS_ENABLED, enabled, userId); + return getBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, true, + getCurrentOrCallingUserId()); } public void setEnabledTrustAgents(Collection<ComponentName> activeTrustAgents) { diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java index 9fa6882..52bbabf 100644 --- a/core/java/com/android/internal/widget/LockPatternView.java +++ b/core/java/com/android/internal/widget/LockPatternView.java @@ -65,8 +65,8 @@ public class LockPatternView extends View { private boolean mDrawingProfilingStarted = false; - private Paint mPaint = new Paint(); - private Paint mPathPaint = new Paint(); + private final Paint mPaint = new Paint(); + private final Paint mPathPaint = new Paint(); /** * How many milliseconds we spend animating each circle of a lock pattern @@ -82,7 +82,7 @@ public class LockPatternView extends View { private static final float DRAG_THRESHHOLD = 0.0f; private OnPatternListener mOnPatternListener; - private ArrayList<Cell> mPattern = new ArrayList<Cell>(9); + private final ArrayList<Cell> mPattern = new ArrayList<Cell>(9); /** * Lookup table for the circles of the pattern we are currently drawing. @@ -90,7 +90,7 @@ public class LockPatternView extends View { * in which case we use this to hold the cells we are drawing for the in * progress animation. */ - private boolean[][] mPatternDrawLookup = new boolean[3][3]; + private final boolean[][] mPatternDrawLookup = new boolean[3][3]; /** * the in progress point: @@ -122,24 +122,27 @@ public class LockPatternView extends View { private int mErrorColor; private int mSuccessColor; - private Interpolator mFastOutSlowInInterpolator; - private Interpolator mLinearOutSlowInInterpolator; + private final Interpolator mFastOutSlowInInterpolator; + private final Interpolator mLinearOutSlowInInterpolator; /** * Represents a cell in the 3 X 3 matrix of the unlock pattern view. */ - public static class Cell { - int row; - int column; + public static final class Cell { + final int row; + final int column; // keep # objects limited to 9 - static Cell[][] sCells = new Cell[3][3]; - static { + private static final Cell[][] sCells = createCells(); + + private static Cell[][] createCells() { + Cell[][] res = new Cell[3][3]; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - sCells[i][j] = new Cell(i, j); + res[i][j] = new Cell(i, j); } } + return res; } /** @@ -160,11 +163,7 @@ public class LockPatternView extends View { return column; } - /** - * @param row The row of the cell. - * @param column The column of the cell. - */ - public static synchronized Cell of(int row, int column) { + public static Cell of(int row, int column) { checkRange(row, column); return sCells[row][column]; } @@ -178,6 +177,7 @@ public class LockPatternView extends View { } } + @Override public String toString() { return "(row=" + row + ",clmn=" + column + ")"; } @@ -722,7 +722,7 @@ public class LockPatternView extends View { handleActionDown(event); return true; case MotionEvent.ACTION_UP: - handleActionUp(event); + handleActionUp(); return true; case MotionEvent.ACTION_MOVE: handleActionMove(event); @@ -812,7 +812,7 @@ public class LockPatternView extends View { announceForAccessibility(mContext.getString(resId)); } - private void handleActionUp(MotionEvent event) { + private void handleActionUp() { // report pattern detected if (!mPattern.isEmpty()) { mPatternInProgress = false; @@ -1119,12 +1119,15 @@ public class LockPatternView extends View { dest.writeValue(mTactileFeedbackEnabled); } + @SuppressWarnings({ "unused", "hiding" }) // Found using reflection public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() { + @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); } + @Override public SavedState[] newArray(int size) { return new SavedState[size]; } diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java index d6bd1d6..a306697 100644 --- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java +++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java @@ -150,7 +150,9 @@ public class ScrollingTabContainerView extends HorizontalScrollView addView(mTabSpinner, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)); if (mTabSpinner.getAdapter() == null) { - mTabSpinner.setAdapter(new TabAdapter()); + final TabAdapter adapter = new TabAdapter(mContext); + adapter.setDropDownViewContext(mTabSpinner.getPopupContext()); + mTabSpinner.setAdapter(adapter); } if (mTabSelector != null) { removeCallbacks(mTabSelector); @@ -276,8 +278,8 @@ public class ScrollingTabContainerView extends HorizontalScrollView } } - private TabView createTabView(ActionBar.Tab tab, boolean forAdapter) { - final TabView tabView = new TabView(getContext(), tab, forAdapter); + private TabView createTabView(Context context, ActionBar.Tab tab, boolean forAdapter) { + final TabView tabView = new TabView(context, tab, forAdapter); if (forAdapter) { tabView.setBackgroundDrawable(null); tabView.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams.MATCH_PARENT, @@ -294,7 +296,7 @@ public class ScrollingTabContainerView extends HorizontalScrollView } public void addTab(ActionBar.Tab tab, boolean setSelected) { - TabView tabView = createTabView(tab, false); + TabView tabView = createTabView(mContext, tab, false); mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1)); if (mTabSpinner != null) { @@ -309,7 +311,7 @@ public class ScrollingTabContainerView extends HorizontalScrollView } public void addTab(ActionBar.Tab tab, int position, boolean setSelected) { - final TabView tabView = createTabView(tab, false); + final TabView tabView = createTabView(mContext, tab, false); mTabLayout.addView(tabView, position, new LinearLayout.LayoutParams( 0, LayoutParams.MATCH_PARENT, 1)); if (mTabSpinner != null) { @@ -391,15 +393,15 @@ public class ScrollingTabContainerView extends HorizontalScrollView } @Override - public void onInitializeAccessibilityEvent(AccessibilityEvent event) { - super.onInitializeAccessibilityEvent(event); + public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) { + super.onInitializeAccessibilityEventInternal(event); // This view masquerades as an action bar tab. event.setClassName(ActionBar.Tab.class.getName()); } @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(info); + public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfoInternal(info); // This view masquerades as an action bar tab. info.setClassName(ActionBar.Tab.class.getName()); } @@ -514,6 +516,16 @@ public class ScrollingTabContainerView extends HorizontalScrollView } private class TabAdapter extends BaseAdapter { + private Context mDropDownContext; + + public TabAdapter(Context context) { + setDropDownViewContext(context); + } + + public void setDropDownViewContext(Context context) { + mDropDownContext = context; + } + @Override public int getCount() { return mTabLayout.getChildCount(); @@ -532,7 +544,18 @@ public class ScrollingTabContainerView extends HorizontalScrollView @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { - convertView = createTabView((ActionBar.Tab) getItem(position), true); + convertView = createTabView(mContext, (ActionBar.Tab) getItem(position), true); + } else { + ((TabView) convertView).bindTab((ActionBar.Tab) getItem(position)); + } + return convertView; + } + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = createTabView(mDropDownContext, + (ActionBar.Tab) getItem(position), true); } else { ((TabView) convertView).bindTab((ActionBar.Tab) getItem(position)); } diff --git a/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java new file mode 100644 index 0000000..3f4b980 --- /dev/null +++ b/core/java/com/android/server/backup/AccountSyncSettingsBackupHelper.java @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2014 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.android.server.backup; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.app.backup.BackupDataInputStream; +import android.app.backup.BackupDataOutput; +import android.app.backup.BackupHelper; +import android.content.ContentResolver; +import android.content.Context; +import android.content.SyncAdapterType; +import android.content.SyncStatusObserver; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.BufferedOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Helper for backing up account sync settings (whether or not a service should be synced). The + * sync settings are backed up as a JSON object containing all the necessary information for + * restoring the sync settings later. + */ +public class AccountSyncSettingsBackupHelper implements BackupHelper { + + private static final String TAG = "AccountSyncSettingsBackupHelper"; + private static final boolean DEBUG = false; + + private static final int STATE_VERSION = 1; + private static final int MD5_BYTE_SIZE = 16; + private static final int SYNC_REQUEST_LATCH_TIMEOUT_SECONDS = 1; + + private static final String JSON_FORMAT_HEADER_KEY = "account_data"; + private static final String JSON_FORMAT_ENCODING = "UTF-8"; + private static final int JSON_FORMAT_VERSION = 1; + + private static final String KEY_VERSION = "version"; + private static final String KEY_MASTER_SYNC_ENABLED = "masterSyncEnabled"; + private static final String KEY_ACCOUNTS = "accounts"; + private static final String KEY_ACCOUNT_NAME = "name"; + private static final String KEY_ACCOUNT_TYPE = "type"; + private static final String KEY_ACCOUNT_AUTHORITIES = "authorities"; + private static final String KEY_AUTHORITY_NAME = "name"; + private static final String KEY_AUTHORITY_SYNC_STATE = "syncState"; + private static final String KEY_AUTHORITY_SYNC_ENABLED = "syncEnabled"; + + private Context mContext; + private AccountManager mAccountManager; + + public AccountSyncSettingsBackupHelper(Context context) { + mContext = context; + mAccountManager = AccountManager.get(mContext); + } + + /** + * Take a snapshot of the current account sync settings and write them to the given output. + */ + @Override + public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput output, + ParcelFileDescriptor newState) { + try { + JSONObject dataJSON = serializeAccountSyncSettingsToJSON(); + + if (DEBUG) { + Log.d(TAG, "Account sync settings JSON: " + dataJSON); + } + + // Encode JSON data to bytes. + byte[] dataBytes = dataJSON.toString().getBytes(JSON_FORMAT_ENCODING); + byte[] oldMd5Checksum = readOldMd5Checksum(oldState); + byte[] newMd5Checksum = generateMd5Checksum(dataBytes); + if (!Arrays.equals(oldMd5Checksum, newMd5Checksum)) { + int dataSize = dataBytes.length; + output.writeEntityHeader(JSON_FORMAT_HEADER_KEY, dataSize); + output.writeEntityData(dataBytes, dataSize); + + Log.i(TAG, "Backup successful."); + } else { + Log.i(TAG, "Old and new MD5 checksums match. Skipping backup."); + } + + writeNewMd5Checksum(newState, newMd5Checksum); + } catch (JSONException | IOException | NoSuchAlgorithmException e) { + Log.e(TAG, "Couldn't backup account sync settings\n" + e); + } + } + + /** + * Fetch and serialize Account and authority information as a JSON Array. + */ + private JSONObject serializeAccountSyncSettingsToJSON() throws JSONException { + Account[] accounts = mAccountManager.getAccounts(); + SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypesAsUser( + mContext.getUserId()); + + // Create a map of Account types to authorities. Later this will make it easier for us to + // generate our JSON. + HashMap<String, List<String>> accountTypeToAuthorities = new HashMap<String, + List<String>>(); + for (SyncAdapterType syncAdapter : syncAdapters) { + // Skip adapters that aren’t visible to the user. + if (!syncAdapter.isUserVisible()) { + continue; + } + if (!accountTypeToAuthorities.containsKey(syncAdapter.accountType)) { + accountTypeToAuthorities.put(syncAdapter.accountType, new ArrayList<String>()); + } + accountTypeToAuthorities.get(syncAdapter.accountType).add(syncAdapter.authority); + } + + // Generate JSON. + JSONObject backupJSON = new JSONObject(); + backupJSON.put(KEY_VERSION, JSON_FORMAT_VERSION); + backupJSON.put(KEY_MASTER_SYNC_ENABLED, ContentResolver.getMasterSyncAutomatically()); + + JSONArray accountJSONArray = new JSONArray(); + for (Account account : accounts) { + List<String> authorities = accountTypeToAuthorities.get(account.type); + + // We ignore Accounts that don't have any authorities because there would be no sync + // settings for us to restore. + if (authorities == null || authorities.isEmpty()) { + continue; + } + + JSONObject accountJSON = new JSONObject(); + accountJSON.put(KEY_ACCOUNT_NAME, account.name); + accountJSON.put(KEY_ACCOUNT_TYPE, account.type); + + // Add authorities for this Account type and check whether or not sync is enabled. + JSONArray authoritiesJSONArray = new JSONArray(); + for (String authority : authorities) { + int syncState = ContentResolver.getIsSyncable(account, authority); + boolean syncEnabled = ContentResolver.getSyncAutomatically(account, authority); + + JSONObject authorityJSON = new JSONObject(); + authorityJSON.put(KEY_AUTHORITY_NAME, authority); + authorityJSON.put(KEY_AUTHORITY_SYNC_STATE, syncState); + authorityJSON.put(KEY_AUTHORITY_SYNC_ENABLED, syncEnabled); + authoritiesJSONArray.put(authorityJSON); + } + accountJSON.put(KEY_ACCOUNT_AUTHORITIES, authoritiesJSONArray); + + accountJSONArray.put(accountJSON); + } + backupJSON.put(KEY_ACCOUNTS, accountJSONArray); + + return backupJSON; + } + + /** + * Read the MD5 checksum from the old state. + * + * @return the old MD5 checksum + */ + private byte[] readOldMd5Checksum(ParcelFileDescriptor oldState) throws IOException { + DataInputStream dataInput = new DataInputStream( + new FileInputStream(oldState.getFileDescriptor())); + + byte[] oldMd5Checksum = new byte[MD5_BYTE_SIZE]; + try { + int stateVersion = dataInput.readInt(); + if (stateVersion <= STATE_VERSION) { + // If the state version is a version we can understand then read the MD5 sum, + // otherwise we return an empty byte array for the MD5 sum which will force a + // backup. + for (int i = 0; i < MD5_BYTE_SIZE; i++) { + oldMd5Checksum[i] = dataInput.readByte(); + } + } else { + Log.i(TAG, "Backup state version is: " + stateVersion + + " (support only up to version " + STATE_VERSION + ")"); + } + } catch (EOFException eof) { + // Initial state may be empty. + } finally { + dataInput.close(); + } + return oldMd5Checksum; + } + + /** + * Write the given checksum to the file descriptor. + */ + private void writeNewMd5Checksum(ParcelFileDescriptor newState, byte[] md5Checksum) + throws IOException { + DataOutputStream dataOutput = new DataOutputStream( + new BufferedOutputStream(new FileOutputStream(newState.getFileDescriptor()))); + + dataOutput.writeInt(STATE_VERSION); + dataOutput.write(md5Checksum); + dataOutput.close(); + } + + private byte[] generateMd5Checksum(byte[] data) throws NoSuchAlgorithmException { + if (data == null) { + return null; + } + + MessageDigest md5 = MessageDigest.getInstance("MD5"); + return md5.digest(data); + } + + /** + * Restore account sync settings from the given data input stream. + */ + @Override + public void restoreEntity(BackupDataInputStream data) { + byte[] dataBytes = new byte[data.size()]; + try { + // Read the data and convert it to a String. + data.read(dataBytes); + String dataString = new String(dataBytes, JSON_FORMAT_ENCODING); + + // Convert data to a JSON object. + JSONObject dataJSON = new JSONObject(dataString); + boolean masterSyncEnabled = dataJSON.getBoolean(KEY_MASTER_SYNC_ENABLED); + JSONArray accountJSONArray = dataJSON.getJSONArray(KEY_ACCOUNTS); + + boolean currentMasterSyncEnabled = ContentResolver.getMasterSyncAutomatically(); + if (currentMasterSyncEnabled) { + // Disable master sync to prevent any syncs from running. + ContentResolver.setMasterSyncAutomatically(false); + } + + try { + HashSet<Account> currentAccounts = getAccountsHashSet(); + for (int i = 0; i < accountJSONArray.length(); i++) { + JSONObject accountJSON = (JSONObject) accountJSONArray.get(i); + String accountName = accountJSON.getString(KEY_ACCOUNT_NAME); + String accountType = accountJSON.getString(KEY_ACCOUNT_TYPE); + + Account account = new Account(accountName, accountType); + + // Check if the account already exists. Accounts that don't exist on the device + // yet won't be restored. + if (currentAccounts.contains(account)) { + restoreExistingAccountSyncSettingsFromJSON(accountJSON); + } + } + } finally { + // Set the master sync preference to the value from the backup set. + ContentResolver.setMasterSyncAutomatically(masterSyncEnabled); + } + + Log.i(TAG, "Restore successful."); + } catch (IOException | JSONException e) { + Log.e(TAG, "Couldn't restore account sync settings\n" + e); + } + } + + /** + * Helper method - fetch accounts and return them as a HashSet. + * + * @return Accounts in a HashSet. + */ + private HashSet<Account> getAccountsHashSet() { + Account[] accounts = mAccountManager.getAccounts(); + HashSet<Account> accountHashSet = new HashSet<Account>(); + for (Account account : accounts) { + accountHashSet.add(account); + } + return accountHashSet; + } + + /** + * Restore account sync settings using the given JSON. This function won't work if the account + * doesn't exist yet. + */ + private void restoreExistingAccountSyncSettingsFromJSON(JSONObject accountJSON) + throws JSONException { + // Restore authorities. + JSONArray authorities = accountJSON.getJSONArray(KEY_ACCOUNT_AUTHORITIES); + String accountName = accountJSON.getString(KEY_ACCOUNT_NAME); + String accountType = accountJSON.getString(KEY_ACCOUNT_TYPE); + final Account account = new Account(accountName, accountType); + for (int i = 0; i < authorities.length(); i++) { + JSONObject authority = (JSONObject) authorities.get(i); + final String authorityName = authority.getString(KEY_AUTHORITY_NAME); + boolean syncEnabled = authority.getBoolean(KEY_AUTHORITY_SYNC_ENABLED); + + // Cancel any active syncs. + if (ContentResolver.isSyncActive(account, authorityName)) { + ContentResolver.cancelSync(account, authorityName); + } + + boolean overwriteSync = true; + Bundle initializationExtras = createSyncInitializationBundle(); + int currentSyncState = ContentResolver.getIsSyncable(account, authorityName); + if (currentSyncState < 0) { + // Requesting a sync is an asynchronous operation, so we setup a countdown latch to + // wait for it to finish. Initialization syncs are generally very brief and + // shouldn't take too much time to finish. + final CountDownLatch latch = new CountDownLatch(1); + Object syncStatusObserverHandle = ContentResolver.addStatusChangeListener( + ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE, new SyncStatusObserver() { + @Override + public void onStatusChanged(int which) { + if (!ContentResolver.isSyncActive(account, authorityName)) { + latch.countDown(); + } + } + }); + + // If we set sync settings for a sync that hasn't been initialized yet, we run the + // risk of having our changes overwritten later on when the sync gets initialized. + // To prevent this from happening we will manually initiate the sync adapter. We + // also explicitly pass in a Bundle with SYNC_EXTRAS_INITIALIZE to prevent a data + // sync from running after the initialization sync. Two syncs will be scheduled, but + // the second one (data sync) will override the first one (initialization sync) and + // still behave as an initialization sync because of the Bundle. + ContentResolver.requestSync(account, authorityName, initializationExtras); + + boolean done = false; + try { + done = latch.await(SYNC_REQUEST_LATCH_TIMEOUT_SECONDS, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Log.e(TAG, "CountDownLatch interrupted\n" + e); + done = false; + } + if (!done) { + overwriteSync = false; + Log.i(TAG, "CountDownLatch timed out, skipping '" + authorityName + + "' authority."); + } + ContentResolver.removeStatusChangeListener(syncStatusObserverHandle); + } + + if (overwriteSync) { + ContentResolver.setSyncAutomatically(account, authorityName, syncEnabled); + Log.i(TAG, "Set sync automatically for '" + authorityName + "': " + syncEnabled); + } + } + } + + private Bundle createSyncInitializationBundle() { + Bundle extras = new Bundle(); + extras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true); + return extras; + } + + @Override + public void writeNewStateDescription(ParcelFileDescriptor newState) { + + } +} diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java index 35a1a5a..b5f2f37 100644 --- a/core/java/com/android/server/backup/SystemBackupAgent.java +++ b/core/java/com/android/server/backup/SystemBackupAgent.java @@ -86,6 +86,8 @@ public class SystemBackupAgent extends BackupAgentHelper { } addHelper("wallpaper", new WallpaperBackupHelper(SystemBackupAgent.this, files, keys)); addHelper("recents", new RecentsBackupHelper(SystemBackupAgent.this)); + addHelper("account_sync_settings", + new AccountSyncSettingsBackupHelper(SystemBackupAgent.this)); super.onBackup(oldState, data, newState); } @@ -118,6 +120,8 @@ public class SystemBackupAgent extends BackupAgentHelper { new String[] { WALLPAPER_IMAGE }, new String[] { WALLPAPER_IMAGE_KEY} )); addHelper("recents", new RecentsBackupHelper(SystemBackupAgent.this)); + addHelper("account_sync_settings", + new AccountSyncSettingsBackupHelper(SystemBackupAgent.this)); try { super.onRestore(data, appVersionCode, newState); |