Merge "Remove restrictions PIN functionality" into mnc-dev

This commit is contained in:
Fyodor Kupolov
2015-05-08 23:10:29 +00:00
committed by Android (Google) Code Review
8 changed files with 5 additions and 443 deletions

View File

@@ -23633,7 +23633,7 @@ package android.os {
method public boolean isUserAGoat();
method public boolean isUserRunning(android.os.UserHandle);
method public boolean isUserRunningOrStopping(android.os.UserHandle);
method public boolean setRestrictionsChallenge(java.lang.String);
method public deprecated boolean setRestrictionsChallenge(java.lang.String);
method public deprecated void setUserRestriction(java.lang.String, boolean);
method public deprecated void setUserRestrictions(android.os.Bundle);
method public deprecated void setUserRestrictions(android.os.Bundle, android.os.UserHandle);

View File

@@ -25544,7 +25544,7 @@ package android.os {
method public boolean isUserAGoat();
method public boolean isUserRunning(android.os.UserHandle);
method public boolean isUserRunningOrStopping(android.os.UserHandle);
method public boolean setRestrictionsChallenge(java.lang.String);
method public deprecated boolean setRestrictionsChallenge(java.lang.String);
method public deprecated void setUserRestriction(java.lang.String, boolean);
method public deprecated void setUserRestrictions(android.os.Bundle);
method public deprecated void setUserRestrictions(android.os.Bundle, android.os.UserHandle);

View File

@@ -2588,21 +2588,6 @@ public class Intent implements Parcelable, Cloneable {
public static final String ACTION_GET_RESTRICTION_ENTRIES =
"android.intent.action.GET_RESTRICTION_ENTRIES";
/**
* @hide
* Activity to challenge the user for a PIN that was configured when setting up
* restrictions. Restrictions include blocking of apps and preventing certain user operations,
* controlled by {@link android.os.UserManager#setUserRestrictions(Bundle).
* Launch the activity using
* {@link android.app.Activity#startActivityForResult(Intent, int)} and check if the
* result is {@link android.app.Activity#RESULT_OK} for a successful response to the
* challenge.<p/>
* Before launching this activity, make sure that there is a PIN in effect, by calling
* {@link android.os.UserManager#hasRestrictionsChallenge()}.
*/
public static final String ACTION_RESTRICTIONS_CHALLENGE =
"android.intent.action.RESTRICTIONS_CHALLENGE";
/**
* Sent the first time a user is starting, to allow system apps to
* perform one time initialization. (This will not be seen by third

View File

@@ -49,9 +49,6 @@ interface IUserManager {
int userHandle);
Bundle getApplicationRestrictions(in String packageName);
Bundle getApplicationRestrictionsForUser(in String packageName, int userHandle);
boolean setRestrictionsChallenge(in String newPin);
int checkRestrictionsChallenge(in String pin);
boolean hasRestrictionsChallenge();
void removeRestrictions();
void setDefaultGuestRestrictions(in Bundle restrictions);
Bundle getDefaultGuestRestrictions();

View File

@@ -744,7 +744,7 @@ public class UserManager {
* @see #getSerialNumberForUser(UserHandle)
*/
public UserHandle getUserForSerialNumber(long serialNumber) {
int ident = getUserHandle((int)serialNumber);
int ident = getUserHandle((int) serialNumber);
return ident >= 0 ? new UserHandle(ident) : null;
}
@@ -1252,49 +1252,10 @@ public class UserManager {
* apps and requires the MANAGE_USERS permission.
* @param newPin the PIN to use for challenge dialogs.
* @return Returns true if the challenge PIN was set successfully.
* @deprecated The restrictions PIN functionality is no longer provided by the system.
* This method is preserved for backwards compatibility reasons and always returns false.
*/
public boolean setRestrictionsChallenge(String newPin) {
try {
return mService.setRestrictionsChallenge(newPin);
} catch (RemoteException re) {
Log.w(TAG, "Could not change restrictions pin");
}
return false;
}
/**
* @hide
* @param pin The PIN to verify, or null to get the number of milliseconds to wait for before
* allowing the user to enter the PIN.
* @return Returns a positive number (including zero) for how many milliseconds before
* you can accept another PIN, when the input is null or the input doesn't match the saved PIN.
* Returns {@link #PIN_VERIFICATION_SUCCESS} if the input matches the saved PIN. Returns
* {@link #PIN_VERIFICATION_FAILED_NOT_SET} if there is no PIN set.
*/
public int checkRestrictionsChallenge(String pin) {
try {
return mService.checkRestrictionsChallenge(pin);
} catch (RemoteException re) {
Log.w(TAG, "Could not check restrictions pin");
}
return PIN_VERIFICATION_FAILED_INCORRECT;
}
/**
* @hide
* Checks whether the user has restrictions that are PIN-protected. An application that
* participates in restrictions can check if the owner has requested a PIN challenge for
* any restricted operations. If there is a PIN in effect, the application should launch
* the PIN challenge activity {@link android.content.Intent#ACTION_RESTRICTIONS_CHALLENGE}.
* @see android.content.Intent#ACTION_RESTRICTIONS_CHALLENGE
* @return whether a restrictions PIN is in effect.
*/
public boolean hasRestrictionsChallenge() {
try {
return mService.hasRestrictionsChallenge();
} catch (RemoteException re) {
Log.w(TAG, "Could not change restrictions pin");
}
return false;
}

View File

@@ -1,181 +0,0 @@
/*
* Copyright (C) 2013 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.internal.app;
import android.content.Context;
import android.os.Bundle;
import android.os.UserManager;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import com.android.internal.R;
/**
* This activity is launched by Settings and other apps to either create a new PIN or
* challenge for an existing PIN. The PIN is maintained by UserManager.
*/
public class RestrictionsPinActivity extends AlertActivity
implements OnClickListener, TextWatcher, OnEditorActionListener {
protected UserManager mUserManager;
protected boolean mHasRestrictionsPin;
protected EditText mPinText;
protected TextView mPinErrorMessage;
private Button mOkButton;
private Button mCancelButton;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
mHasRestrictionsPin = mUserManager.hasRestrictionsChallenge();
initUi();
setupAlert();
}
protected void initUi() {
AlertController.AlertParams ap = mAlertParams;
ap.mTitle = getString(R.string.restr_pin_enter_admin_pin);
LayoutInflater inflater =
(LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ap.mView = inflater.inflate(R.layout.restrictions_pin_challenge, null);
mPinErrorMessage = (TextView) ap.mView.findViewById(R.id.pin_error_message);
mPinText = (EditText) ap.mView.findViewById(R.id.pin_text);
mOkButton = (Button) ap.mView.findViewById(R.id.pin_ok_button);
mCancelButton = (Button) ap.mView.findViewById(R.id.pin_cancel_button);
mPinText.addTextChangedListener(this);
mOkButton.setOnClickListener(this);
mCancelButton.setOnClickListener(this);
}
protected boolean verifyingPin() {
return true;
}
public void onResume() {
super.onResume();
setPositiveButtonState(false);
boolean hasPin = mUserManager.hasRestrictionsChallenge();
if (hasPin) {
mPinErrorMessage.setVisibility(View.INVISIBLE);
mPinText.setOnEditorActionListener(this);
updatePinTimer(-1);
} else if (verifyingPin()) {
setResult(RESULT_OK);
finish();
}
}
protected void setPositiveButtonState(boolean enabled) {
mOkButton.setEnabled(enabled);
}
private boolean updatePinTimer(int pinTimerMs) {
if (pinTimerMs < 0) {
pinTimerMs = mUserManager.checkRestrictionsChallenge(null);
}
boolean enableInput;
if (pinTimerMs >= 200) {
// Do the count down timer for less than a minute, otherwise just say try again later.
if (pinTimerMs <= 60000) {
final int seconds = (pinTimerMs + 200) / 1000;
final String formatString = getResources().getQuantityString(
R.plurals.restr_pin_countdown,
seconds);
mPinErrorMessage.setText(String.format(formatString, seconds));
} else {
mPinErrorMessage.setText(R.string.restr_pin_try_later);
}
enableInput = false;
mPinErrorMessage.setVisibility(View.VISIBLE);
mPinText.setText("");
mPinText.postDelayed(mCountdownRunnable, Math.min(1000, pinTimerMs));
} else {
enableInput = true;
mPinErrorMessage.setText(R.string.restr_pin_incorrect);
}
mPinText.setEnabled(enableInput);
setPositiveButtonState(enableInput);
return enableInput;
}
protected void performPositiveButtonAction() {
int result = mUserManager.checkRestrictionsChallenge(mPinText.getText().toString());
if (result == UserManager.PIN_VERIFICATION_SUCCESS) {
setResult(RESULT_OK);
finish();
} else if (result >= 0) {
mPinErrorMessage.setText(R.string.restr_pin_incorrect);
mPinErrorMessage.setVisibility(View.VISIBLE);
updatePinTimer(result);
mPinText.setText("");
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
CharSequence pin = mPinText.getText();
setPositiveButtonState(pin != null && pin.length() >= 4);
}
@Override
public void afterTextChanged(Editable s) {
}
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
performPositiveButtonAction();
return true;
}
private Runnable mCountdownRunnable = new Runnable() {
public void run() {
if (updatePinTimer(-1)) {
// If we are no longer counting down, clear the message.
mPinErrorMessage.setVisibility(View.INVISIBLE);
}
}
};
@Override
public void onClick(View v) {
if (v == mOkButton) {
performPositiveButtonAction();
} else if (v == mCancelButton) {
setResult(RESULT_CANCELED);
finish();
}
}
}

View File

@@ -2573,17 +2573,6 @@
android:process=":ui">
</activity>
<activity android:name="com.android.internal.app.RestrictionsPinActivity"
android:theme="@style/Theme.Material.DayNight.Dialog.Alert"
android:excludeFromRecents="true"
android:windowSoftInputMode="adjustPan"
android:process=":ui">
<intent-filter android:priority="100">
<action android:name="android.intent.action.RESTRICTIONS_CHALLENGE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<receiver android:name="com.android.server.BootReceiver"
android:primaryUserOnly="true">
<intent-filter android:priority="1000">

View File

@@ -68,9 +68,6 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
@@ -88,10 +85,6 @@ public class UserManagerService extends IUserManager.Stub {
private static final String ATTR_ID = "id";
private static final String ATTR_CREATION_TIME = "created";
private static final String ATTR_LAST_LOGGED_IN_TIME = "lastLoggedIn";
private static final String ATTR_SALT = "salt";
private static final String ATTR_PIN_HASH = "pinHash";
private static final String ATTR_FAILED_ATTEMPTS = "failedAttempts";
private static final String ATTR_LAST_RETRY_MS = "lastAttemptMs";
private static final String ATTR_SERIAL_NO = "serialNumber";
private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber";
private static final String ATTR_PARTIAL = "partial";
@@ -129,17 +122,10 @@ public class UserManagerService extends IUserManager.Stub {
private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
// Number of attempts before jumping to the next BACKOFF_TIMES slot
private static final int BACKOFF_INC_INTERVAL = 5;
// Maximum number of managed profiles permitted is 1. This cannot be increased
// without first making sure that the rest of the framework is prepared for it.
private static final int MAX_MANAGED_PROFILES = 1;
// Amount of time to force the user to wait before entering the PIN again, after failing
// BACKOFF_INC_INTERVAL times.
private static final int[] BACKOFF_TIMES = { 0, 30*1000, 60*1000, 5*60*1000, 30*60*1000 };
static final int WRITE_USER_MSG = 1;
static final int WRITE_USER_DELAY = 2*1000; // 2 seconds
@@ -158,16 +144,6 @@ public class UserManagerService extends IUserManager.Stub {
private final SparseArray<Bundle> mUserRestrictions = new SparseArray<Bundle>();
private final Bundle mGuestRestrictions = new Bundle();
class RestrictionsPinState {
long salt;
String pinHash;
int failedAttempts;
long lastAttemptTime;
}
private final SparseArray<RestrictionsPinState> mRestrictionsPinStates =
new SparseArray<RestrictionsPinState>();
/**
* Set of user IDs being actively removed. Removed IDs linger in this set
* for several seconds to work around a VFS caching issue.
@@ -810,21 +786,6 @@ public class UserManagerService extends IUserManager.Stub {
serializer.attribute(null, ATTR_CREATION_TIME, Long.toString(userInfo.creationTime));
serializer.attribute(null, ATTR_LAST_LOGGED_IN_TIME,
Long.toString(userInfo.lastLoggedInTime));
RestrictionsPinState pinState = mRestrictionsPinStates.get(userInfo.id);
if (pinState != null) {
if (pinState.salt != 0) {
serializer.attribute(null, ATTR_SALT, Long.toString(pinState.salt));
}
if (pinState.pinHash != null) {
serializer.attribute(null, ATTR_PIN_HASH, pinState.pinHash);
}
if (pinState.failedAttempts != 0) {
serializer.attribute(null, ATTR_FAILED_ATTEMPTS,
Integer.toString(pinState.failedAttempts));
serializer.attribute(null, ATTR_LAST_RETRY_MS,
Long.toString(pinState.lastAttemptTime));
}
}
if (userInfo.iconPath != null) {
serializer.attribute(null, ATTR_ICON_PATH, userInfo.iconPath);
}
@@ -944,11 +905,7 @@ public class UserManagerService extends IUserManager.Stub {
String iconPath = null;
long creationTime = 0L;
long lastLoggedInTime = 0L;
long salt = 0L;
String pinHash = null;
int failedAttempts = 0;
int profileGroupId = UserInfo.NO_PROFILE_GROUP_ID;
long lastAttemptTime = 0L;
boolean partial = false;
boolean guestToRemove = false;
Bundle restrictions = new Bundle();
@@ -982,10 +939,6 @@ public class UserManagerService extends IUserManager.Stub {
iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH);
creationTime = readLongAttribute(parser, ATTR_CREATION_TIME, 0);
lastLoggedInTime = readLongAttribute(parser, ATTR_LAST_LOGGED_IN_TIME, 0);
salt = readLongAttribute(parser, ATTR_SALT, 0L);
pinHash = parser.getAttributeValue(null, ATTR_PIN_HASH);
failedAttempts = readIntAttribute(parser, ATTR_FAILED_ATTEMPTS, 0);
lastAttemptTime = readLongAttribute(parser, ATTR_LAST_RETRY_MS, 0L);
profileGroupId = readIntAttribute(parser, ATTR_PROFILE_GROUP_ID,
UserInfo.NO_PROFILE_GROUP_ID);
String valueString = parser.getAttributeValue(null, ATTR_PARTIAL);
@@ -1023,17 +976,6 @@ public class UserManagerService extends IUserManager.Stub {
userInfo.guestToRemove = guestToRemove;
userInfo.profileGroupId = profileGroupId;
mUserRestrictions.append(id, restrictions);
if (salt != 0L) {
RestrictionsPinState pinState = mRestrictionsPinStates.get(id);
if (pinState == null) {
pinState = new RestrictionsPinState();
mRestrictionsPinStates.put(id, pinState);
}
pinState.salt = salt;
pinState.pinHash = pinHash;
pinState.failedAttempts = failedAttempts;
pinState.lastAttemptTime = lastAttemptTime;
}
return userInfo;
} catch (IOException ioe) {
@@ -1435,8 +1377,6 @@ public class UserManagerService extends IUserManager.Stub {
// Remove this user from the list
mUsers.remove(userHandle);
mRestrictionsPinStates.remove(userHandle);
// Remove user file
AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX));
userFile.delete();
@@ -1507,92 +1447,6 @@ public class UserManagerService extends IUserManager.Stub {
}
}
@Override
public boolean setRestrictionsChallenge(String newPin) {
checkManageUsersPermission("Only system can modify the restrictions pin");
int userId = UserHandle.getCallingUserId();
synchronized (mPackagesLock) {
RestrictionsPinState pinState = mRestrictionsPinStates.get(userId);
if (pinState == null) {
pinState = new RestrictionsPinState();
}
if (newPin == null) {
pinState.salt = 0;
pinState.pinHash = null;
} else {
try {
pinState.salt = SecureRandom.getInstance("SHA1PRNG").nextLong();
} catch (NoSuchAlgorithmException e) {
pinState.salt = (long) (Math.random() * Long.MAX_VALUE);
}
pinState.pinHash = passwordToHash(newPin, pinState.salt);
pinState.failedAttempts = 0;
}
mRestrictionsPinStates.put(userId, pinState);
writeUserLocked(mUsers.get(userId));
}
return true;
}
@Override
public int checkRestrictionsChallenge(String pin) {
checkManageUsersPermission("Only system can verify the restrictions pin");
int userId = UserHandle.getCallingUserId();
synchronized (mPackagesLock) {
RestrictionsPinState pinState = mRestrictionsPinStates.get(userId);
// If there's no pin set, return error code
if (pinState == null || pinState.salt == 0 || pinState.pinHash == null) {
return UserManager.PIN_VERIFICATION_FAILED_NOT_SET;
} else if (pin == null) {
// If just checking if user can be prompted, return remaining time
int waitTime = getRemainingTimeForPinAttempt(pinState);
Slog.d(LOG_TAG, "Remaining waittime peek=" + waitTime);
return waitTime;
} else {
int waitTime = getRemainingTimeForPinAttempt(pinState);
Slog.d(LOG_TAG, "Remaining waittime=" + waitTime);
if (waitTime > 0) {
return waitTime;
}
if (passwordToHash(pin, pinState.salt).equals(pinState.pinHash)) {
pinState.failedAttempts = 0;
scheduleWriteUserLocked(mUsers.get(userId));
return UserManager.PIN_VERIFICATION_SUCCESS;
} else {
pinState.failedAttempts++;
pinState.lastAttemptTime = System.currentTimeMillis();
scheduleWriteUserLocked(mUsers.get(userId));
return waitTime;
}
}
}
}
private int getRemainingTimeForPinAttempt(RestrictionsPinState pinState) {
int backoffIndex = Math.min(pinState.failedAttempts / BACKOFF_INC_INTERVAL,
BACKOFF_TIMES.length - 1);
int backoffTime = (pinState.failedAttempts % BACKOFF_INC_INTERVAL) == 0 ?
BACKOFF_TIMES[backoffIndex] : 0;
return (int) Math.max(backoffTime + pinState.lastAttemptTime - System.currentTimeMillis(),
0);
}
@Override
public boolean hasRestrictionsChallenge() {
int userId = UserHandle.getCallingUserId();
synchronized (mPackagesLock) {
return hasRestrictionsPinLocked(userId);
}
}
private boolean hasRestrictionsPinLocked(int userId) {
RestrictionsPinState pinState = mRestrictionsPinStates.get(userId);
if (pinState == null || pinState.salt == 0 || pinState.pinHash == null) {
return false;
}
return true;
}
@Override
public void removeRestrictions() {
checkManageUsersPermission("Only system can remove restrictions");
@@ -1604,8 +1458,6 @@ public class UserManagerService extends IUserManager.Stub {
synchronized (mPackagesLock) {
// Remove all user restrictions
setUserRestrictions(new Bundle(), userHandle);
// Remove restrictions pin
setRestrictionsChallenge(null);
// Remove any app restrictions
cleanAppRestrictions(userHandle);
}
@@ -1637,42 +1489,6 @@ public class UserManagerService extends IUserManager.Stub {
}
});
}
/*
* Generate a hash for the given password. To avoid brute force attacks, we use a salted hash.
* Not the most secure, but it is at least a second level of protection. First level is that
* the file is in a location only readable by the system process.
* @param password the password.
* @param salt the randomly generated salt
* @return the hash of the pattern in a String.
*/
private String passwordToHash(String password, long salt) {
if (password == null) {
return null;
}
String algo = null;
String hashed = salt + password;
try {
byte[] saltedPassword = (password + salt).getBytes();
byte[] sha1 = MessageDigest.getInstance(algo = "SHA-1").digest(saltedPassword);
byte[] md5 = MessageDigest.getInstance(algo = "MD5").digest(saltedPassword);
hashed = toHex(sha1) + toHex(md5);
} catch (NoSuchAlgorithmException e) {
Log.w(LOG_TAG, "Failed to encode string because of missing algorithm: " + algo);
}
return hashed;
}
private static String toHex(byte[] ary) {
final String hex = "0123456789ABCDEF";
String ret = "";
for (int i = 0; i < ary.length; i++) {
ret += hex.charAt((ary[i] >> 4) & 0xf);
ret += hex.charAt(ary[i] & 0xf);
}
return ret;
}
private int getUidForPackage(String packageName) {
long ident = Binder.clearCallingIdentity();
try {
@@ -1958,11 +1774,6 @@ public class UserManagerService extends IUserManager.Stub {
return RESTRICTIONS_FILE_PREFIX + packageName + XML_SUFFIX;
}
private String restrictionsFileNameToPackage(String fileName) {
return fileName.substring(RESTRICTIONS_FILE_PREFIX.length(),
(int) (fileName.length() - XML_SUFFIX.length()));
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)