Merge "Reintroduce the net.dns system properties <TEMP>"
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d80598c..c507245 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -666,6 +666,15 @@
public static final int INSTALL_FAILED_INTERNAL_ERROR = -110;
/**
+ * Installation failed return code: this is passed to the {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
+ * if the system failed to install the package because the user is restricted from installing
+ * apps.
+ * @hide
+ */
+ public static final int INSTALL_FAILED_USER_RESTRICTED = -111;
+
+ /**
* Flag parameter for {@link #deletePackage} to indicate that you don't want to delete the
* package's data directory.
*
@@ -710,6 +719,15 @@
public static final int DELETE_FAILED_DEVICE_POLICY_MANAGER = -2;
/**
+ * Deletion failed return code: this is passed to the
+ * {@link IPackageDeleteObserver} by {@link #deletePackage()} if the system
+ * failed to delete the package since the user is restricted.
+ *
+ * @hide
+ */
+ public static final int DELETE_FAILED_USER_RESTRICTED = -3;
+
+ /**
* Return code that is passed to the {@link IPackageMoveObserver} by
* {@link #movePackage(android.net.Uri, IPackageMoveObserver)} when the
* package has been successfully moved by the system.
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index ec02ae0..34c9740 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -17,6 +17,7 @@
package android.os;
+import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.content.pm.UserInfo;
import android.graphics.Bitmap;
@@ -37,4 +38,6 @@
void wipeUser(int userHandle);
int getUserSerialNumber(int userHandle);
int getUserHandle(int userSerialNumber);
+ Bundle getUserRestrictions(int userHandle);
+ void setUserRestrictions(in Bundle restrictions, int userHandle);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index d73f99a..e4a5e7f 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -35,6 +35,42 @@
private final IUserManager mService;
private final Context mContext;
+ /**
+ * @hide
+ * Key for user restrictions. Specifies if a user is allowed to add or remove accounts.
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String ALLOW_MODIFY_ACCOUNTS = "modify_accounts";
+
+ /**
+ * @hide
+ * Key for user restrictions. Specifies if a user is allowed to change Wi-Fi access points.
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String ALLOW_CONFIG_WIFI = "config_wifi";
+
+ /**
+ * @hide
+ * Key for user restrictions. Specifies if a user is allowed to install applications.
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String ALLOW_INSTALL_APPS = "install_apps";
+
+ /**
+ * @hide
+ * Key for user restrictions. Specifies if a user is allowed to uninstall applications.
+ * Type: Boolean
+ * @see #setUserRestrictions(Bundle)
+ * @see #getUserRestrictions()
+ */
+ public static final String ALLOW_UNINSTALL_APPS = "uninstall_apps";
+
/** @hide */
public UserManager(Context context, IUserManager service) {
mService = service;
@@ -132,6 +168,35 @@
}
}
+ /** @hide */
+ public Bundle getUserRestrictions() {
+ return getUserRestrictions(Process.myUserHandle());
+ }
+
+ /** @hide */
+ public Bundle getUserRestrictions(UserHandle userHandle) {
+ try {
+ return mService.getUserRestrictions(userHandle.getIdentifier());
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not get user restrictions", re);
+ return Bundle.EMPTY;
+ }
+ }
+
+ /** @hide */
+ public void setUserRestrictions(Bundle restrictions) {
+ setUserRestrictions(restrictions, Process.myUserHandle());
+ }
+
+ /** @hide */
+ public void setUserRestrictions(Bundle restrictions, UserHandle userHandle) {
+ try {
+ mService.setUserRestrictions(restrictions, userHandle.getIdentifier());
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not set user restrictions", re);
+ }
+ }
+
/**
* Return the serial number for a user. This is a device-unique
* number assigned to that user; if the user is deleted and then a new
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c3eae3a..8b1863b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1868,6 +1868,12 @@
private static final int LAYOUT_DIRECTION_DEFAULT = LAYOUT_DIRECTION_INHERIT;
/**
+ * Default horizontal layout direction.
+ * @hide
+ */
+ static final int LAYOUT_DIRECTION_RESOLVED_DEFAULT = LAYOUT_DIRECTION_LTR;
+
+ /**
* Indicates that the view is tracking some sort of transient state
* that the app should not need to be aware of, but that the framework
* should take special care to preserve.
@@ -1916,6 +1922,12 @@
private static final int TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT;
/**
+ * Default resolved text direction
+ * @hide
+ */
+ static final int TEXT_DIRECTION_RESOLVED_DEFAULT = TEXT_DIRECTION_FIRST_STRONG;
+
+ /**
* Bit shift to get the horizontal layout direction. (bits after LAYOUT_DIRECTION_RESOLVED)
* @hide
*/
@@ -1967,7 +1979,7 @@
* @hide
*/
static final int PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT =
- TEXT_DIRECTION_FIRST_STRONG << PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT;
+ TEXT_DIRECTION_RESOLVED_DEFAULT << PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT;
/*
* Default text alignment. The text alignment of this View is inherited from its parent.
@@ -2026,6 +2038,12 @@
private static final int TEXT_ALIGNMENT_DEFAULT = TEXT_ALIGNMENT_GRAVITY;
/**
+ * Default resolved text alignment
+ * @hide
+ */
+ static final int TEXT_ALIGNMENT_RESOLVED_DEFAULT = TEXT_ALIGNMENT_GRAVITY;
+
+ /**
* Bit shift to get the horizontal layout direction. (bits after DRAG_HOVERED)
* @hide
*/
@@ -2075,7 +2093,7 @@
* Indicates whether if the view text alignment has been resolved to gravity
*/
private static final int PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT =
- TEXT_ALIGNMENT_GRAVITY << PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT;
+ TEXT_ALIGNMENT_RESOLVED_DEFAULT << PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT;
// Accessiblity constants for mPrivateFlags2
@@ -5979,7 +5997,7 @@
final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
if (targetSdkVersion < JELLY_BEAN_MR1) {
mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED;
- return LAYOUT_DIRECTION_LTR;
+ return LAYOUT_DIRECTION_RESOLVED_DEFAULT;
}
return ((mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) ==
PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL) ? LAYOUT_DIRECTION_RTL : LAYOUT_DIRECTION_LTR;
@@ -11805,11 +11823,10 @@
// later to get the correct resolved value
if (!canResolveLayoutDirection()) return false;
- View parent = ((View) mParent);
// Parent has not yet resolved, LTR is still the default
- if (!parent.isLayoutDirectionResolved()) return false;
+ if (!mParent.isLayoutDirectionResolved()) return false;
- if (parent.getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
+ if (mParent.getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL;
}
break;
@@ -11842,8 +11859,7 @@
public boolean canResolveLayoutDirection() {
switch (getRawLayoutDirection()) {
case LAYOUT_DIRECTION_INHERIT:
- return (mParent != null) && (mParent instanceof ViewGroup) &&
- ((ViewGroup) mParent).canResolveLayoutDirection();
+ return (mParent != null) && mParent.canResolveLayoutDirection();
default:
return true;
}
@@ -11871,8 +11887,9 @@
/**
* @return true if layout direction has been resolved.
+ * @hide
*/
- private boolean isLayoutDirectionResolved() {
+ public boolean isLayoutDirectionResolved() {
return (mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_RESOLVED) == PFLAG2_LAYOUT_DIRECTION_RESOLVED;
}
@@ -16905,16 +16922,15 @@
return false;
}
- View parent = ((View) mParent);
// Parent has not yet resolved, so we still return the default
- if (!parent.isTextDirectionResolved()) {
+ if (!mParent.isTextDirectionResolved()) {
mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT;
// Resolution will need to happen again later
return false;
}
// Set current resolved direction to the same value as the parent's one
- final int parentResolvedDirection = parent.getTextDirection();
+ final int parentResolvedDirection = mParent.getTextDirection();
switch (parentResolvedDirection) {
case TEXT_DIRECTION_FIRST_STRONG:
case TEXT_DIRECTION_ANY_RTL:
@@ -16955,12 +16971,13 @@
* Check if text direction resolution can be done.
*
* @return true if text direction resolution can be done otherwise return false.
+ *
+ * @hide
*/
- private boolean canResolveTextDirection() {
+ public boolean canResolveTextDirection() {
switch (getRawTextDirection()) {
case TEXT_DIRECTION_INHERIT:
- return (mParent != null) && (mParent instanceof View) &&
- ((View) mParent).canResolveTextDirection();
+ return (mParent != null) && mParent.canResolveTextDirection();
default:
return true;
}
@@ -16990,8 +17007,10 @@
/**
* @return true if text direction is resolved.
+ *
+ * @hide
*/
- private boolean isTextDirectionResolved() {
+ public boolean isTextDirectionResolved() {
return (mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_RESOLVED) == PFLAG2_TEXT_DIRECTION_RESOLVED;
}
@@ -17114,16 +17133,15 @@
// Resolution will need to happen again later
return false;
}
- View parent = (View) mParent;
// Parent has not yet resolved, so we still return the default
- if (!parent.isTextAlignmentResolved()) {
+ if (!mParent.isTextAlignmentResolved()) {
mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT;
// Resolution will need to happen again later
return false;
}
- final int parentResolvedTextAlignment = parent.getTextAlignment();
+ final int parentResolvedTextAlignment = mParent.getTextAlignment();
switch (parentResolvedTextAlignment) {
case TEXT_ALIGNMENT_GRAVITY:
case TEXT_ALIGNMENT_TEXT_START:
@@ -17168,12 +17186,13 @@
* Check if text alignment resolution can be done.
*
* @return true if text alignment resolution can be done otherwise return false.
+ *
+ * @hide
*/
- private boolean canResolveTextAlignment() {
+ public boolean canResolveTextAlignment() {
switch (getRawTextAlignment()) {
case TEXT_DIRECTION_INHERIT:
- return (mParent != null) && (mParent instanceof View) &&
- ((View) mParent).canResolveTextAlignment();
+ return (mParent != null) && mParent.canResolveTextAlignment();
default:
return true;
}
@@ -17203,8 +17222,10 @@
/**
* @return true if text alignment is resolved.
+ *
+ * @hide
*/
- private boolean isTextAlignmentResolved() {
+ public boolean isTextAlignmentResolved() {
return (mPrivateFlags2 & PFLAG2_TEXT_ALIGNMENT_RESOLVED) == PFLAG2_TEXT_ALIGNMENT_RESOLVED;
}
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index ddff91d..4b70bc0 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -295,4 +295,105 @@
* @hide
*/
public void childAccessibilityStateChanged(View child);
+
+ /**
+ * Tells if this view parent can resolve the layout direction.
+ * See {@link View#setLayoutDirection(int)}
+ *
+ * @return True if this view parent can resolve the layout direction.
+ *
+ * @hide
+ */
+ public boolean canResolveLayoutDirection();
+
+ /**
+ * Tells if this view parent layout direction is resolved.
+ * See {@link View#setLayoutDirection(int)}
+ *
+ * @return True if this view parent layout direction is resolved.
+ *
+ * @hide
+ */
+ public boolean isLayoutDirectionResolved();
+
+ /**
+ * Return this view parent layout direction. See {@link View#getLayoutDirection()}
+ *
+ * @return {@link View#LAYOUT_DIRECTION_RTL} if the layout direction is RTL or returns
+ * {@link View#LAYOUT_DIRECTION_LTR} if the layout direction is not RTL.
+ *
+ * @hide
+ */
+ public int getLayoutDirection();
+
+ /**
+ * Tells if this view parent can resolve the text direction.
+ * See {@link View#setTextDirection(int)}
+ *
+ * @return True if this view parent can resolve the text direction.
+ *
+ * @hide
+ */
+ public boolean canResolveTextDirection();
+
+ /**
+ * Tells if this view parent text direction is resolved.
+ * See {@link View#setTextDirection(int)}
+ *
+ * @return True if this view parent text direction is resolved.
+ *
+ * @hide
+ */
+ public boolean isTextDirectionResolved();
+
+ /**
+ * Return this view parent text direction. See {@link View#getTextDirection()}
+ *
+ * @return the resolved text direction. Returns one of:
+ *
+ * {@link View#TEXT_DIRECTION_FIRST_STRONG}
+ * {@link View#TEXT_DIRECTION_ANY_RTL},
+ * {@link View#TEXT_DIRECTION_LTR},
+ * {@link View#TEXT_DIRECTION_RTL},
+ * {@link View#TEXT_DIRECTION_LOCALE}
+ *
+ * @hide
+ */
+ public int getTextDirection();
+
+ /**
+ * Tells if this view parent can resolve the text alignment.
+ * See {@link View#setTextAlignment(int)}
+ *
+ * @return True if this view parent can resolve the text alignment.
+ *
+ * @hide
+ */
+ public boolean canResolveTextAlignment();
+
+ /**
+ * Tells if this view parent text alignment is resolved.
+ * See {@link View#setTextAlignment(int)}
+ *
+ * @return True if this view parent text alignment is resolved.
+ *
+ * @hide
+ */
+ public boolean isTextAlignmentResolved();
+
+ /**
+ * Return this view parent text alignment. See {@link android.view.View#getTextAlignment()}
+ *
+ * @return the resolved text alignment. Returns one of:
+ *
+ * {@link View#TEXT_ALIGNMENT_GRAVITY},
+ * {@link View#TEXT_ALIGNMENT_CENTER},
+ * {@link View#TEXT_ALIGNMENT_TEXT_START},
+ * {@link View#TEXT_ALIGNMENT_TEXT_END},
+ * {@link View#TEXT_ALIGNMENT_VIEW_START},
+ * {@link View#TEXT_ALIGNMENT_VIEW_END}
+ *
+ * @hide
+ */
+ public int getTextAlignment();
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2df56c3..1d86361 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4829,6 +4829,51 @@
postSendWindowContentChangedCallback(child);
}
+ @Override
+ public boolean canResolveLayoutDirection() {
+ return true;
+ }
+
+ @Override
+ public boolean isLayoutDirectionResolved() {
+ return true;
+ }
+
+ @Override
+ public int getLayoutDirection() {
+ return View.LAYOUT_DIRECTION_RESOLVED_DEFAULT;
+ }
+
+ @Override
+ public boolean canResolveTextDirection() {
+ return true;
+ }
+
+ @Override
+ public boolean isTextDirectionResolved() {
+ return true;
+ }
+
+ @Override
+ public int getTextDirection() {
+ return View.TEXT_DIRECTION_RESOLVED_DEFAULT;
+ }
+
+ @Override
+ public boolean canResolveTextAlignment() {
+ return true;
+ }
+
+ @Override
+ public boolean isTextAlignmentResolved() {
+ return true;
+ }
+
+ @Override
+ public int getTextAlignment() {
+ return View.TEXT_ALIGNMENT_RESOLVED_DEFAULT;
+ }
+
private View getCommonPredecessor(View first, View second) {
if (mAttachInfo != null) {
if (mTempHashSet == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 280b368..ddfaad5 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -45,6 +45,7 @@
import android.os.Process;
import android.provider.MediaStore;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -86,6 +87,8 @@
*/
class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Void,
SaveImageInBackgroundData> {
+ private static final String TAG = "SaveImageInBackgroundTask";
+
private static final String SCREENSHOTS_DIR_NAME = "Screenshots";
private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png";
private static final String SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)";
@@ -138,6 +141,7 @@
(shortSide - mImageHeight) / 2);
c.drawBitmap(data.image, matrix, paint);
c.drawColor(0x40FFFFFF);
+ c.setBitmap(null);
Bitmap croppedIcon = Bitmap.createScaledBitmap(preview, iconSize, iconSize, true);
@@ -167,6 +171,8 @@
mNotificationBuilder.setLargeIcon(croppedIcon);
// But we still don't set it for the expanded view, allowing the smallIcon to show here.
mNotificationStyle.bigLargeIcon(null);
+
+ Log.d(TAG, "SaveImageInBackgroundTask constructor");
}
@Override
@@ -174,6 +180,7 @@
if (params.length != 1) return null;
if (isCancelled()) {
params[0].clearImage();
+ Log.d(TAG, "doInBackground cancelled");
return null;
}
@@ -238,6 +245,7 @@
// mounted
params[0].clearImage();
params[0].result = 1;
+ Log.d(TAG, "doInBackground failed");
}
// Recycle the bitmap data
@@ -245,6 +253,7 @@
image.recycle();
}
+ Log.d(TAG, "doInBackground complete");
return params[0];
}
@@ -253,6 +262,7 @@
if (isCancelled()) {
params.finisher.run();
params.clearImage();
+ Log.d(TAG, "onPostExecute cancelled");
return;
}
@@ -280,6 +290,7 @@
mNotificationManager.notify(mNotificationId, n);
}
params.finisher.run();
+ Log.d(TAG, "onPostExecute complete");
}
}
@@ -290,6 +301,8 @@
* type of gallery?
*/
class GlobalScreenshot {
+ private static final String TAG = "GlobalScreenshot";
+
private static final int SCREENSHOT_NOTIFICATION_ID = 789;
private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130;
private static final int SCREENSHOT_DROP_IN_DURATION = 430;
@@ -381,12 +394,15 @@
// Setup the Camera shutter sound
mCameraSound = new MediaActionSound();
mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
+
+ Log.d(TAG, "GlobalScreenshot constructor");
}
/**
* Creates a new worker thread and saves the screenshot to the media store.
*/
private void saveScreenshotInWorkerThread(Runnable finisher) {
+ Log.d(TAG, "saveScreenshotInWorkerThread");
SaveImageInBackgroundData data = new SaveImageInBackgroundData();
data.context = mContext;
data.image = mScreenBitmap;
@@ -394,6 +410,7 @@
data.finisher = finisher;
if (mSaveInBgTask != null) {
mSaveInBgTask.cancel(false);
+ Log.d(TAG, "saveScreenshotInWorkerThread cancel");
}
mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data, mNotificationManager,
SCREENSHOT_NOTIFICATION_ID).execute(data);
@@ -418,6 +435,8 @@
* Takes a screenshot of the current display and shows an animation.
*/
void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {
+ Log.d(TAG, "takeScreenshot");
+
// We need to orient the screenshot correctly (and the Surface api seems to take screenshots
// only in the natural orientation of the device :!)
mDisplay.getRealMetrics(mDisplayMetrics);
@@ -431,6 +450,8 @@
mDisplayMatrix.mapPoints(dims);
dims[0] = Math.abs(dims[0]);
dims[1] = Math.abs(dims[1]);
+
+ Log.d(TAG, "takeScreenshot requiresRotation");
}
// Take the screenshot
@@ -438,6 +459,7 @@
if (mScreenBitmap == null) {
notifyScreenshotError(mContext, mNotificationManager);
finisher.run();
+ Log.d(TAG, "takeScreenshot null bitmap");
return;
}
@@ -451,7 +473,10 @@
c.translate(-dims[0] / 2, -dims[1] / 2);
c.drawBitmap(mScreenBitmap, 0, 0, null);
c.setBitmap(null);
+ // Recycle the previous bitmap
+ mScreenBitmap.recycle();
mScreenBitmap = ss;
+ Log.d(TAG, "takeScreenshot rotation bitmap created");
}
// Optimizations
@@ -461,6 +486,7 @@
// Start the post-screenshot animation
startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
statusBarVisible, navBarVisible);
+ Log.d(TAG, "takeScreenshot startedAnimation");
}
@@ -469,6 +495,7 @@
*/
private void startAnimation(final Runnable finisher, int w, int h, boolean statusBarVisible,
boolean navBarVisible) {
+ Log.d(TAG, "startAnimation");
// Add the view for the animation
mScreenshotView.setImageBitmap(mScreenBitmap);
mScreenshotLayout.requestFocus();
@@ -477,9 +504,11 @@
if (mScreenshotAnimation != null) {
mScreenshotAnimation.end();
mScreenshotAnimation.removeAllListeners();
+ Log.d(TAG, "startAnimation reset previous animations");
}
mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
+ Log.d(TAG, "startAnimation layout added to WM");
ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation();
ValueAnimator screenshotFadeOutAnim = createScreenshotDropOutAnimation(w, h,
statusBarVisible, navBarVisible);
@@ -495,6 +524,7 @@
// Clear any references to the bitmap
mScreenBitmap = null;
mScreenshotView.setImageBitmap(null);
+ Log.d(TAG, "startAnimation onAnimationEnd");
}
});
mScreenshotLayout.post(new Runnable() {
@@ -506,6 +536,7 @@
mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
mScreenshotView.buildLayer();
mScreenshotAnimation.start();
+ Log.d(TAG, "startAnimation post runnable");
}
});
}
@@ -643,6 +674,7 @@
}
static void notifyScreenshotError(Context context, NotificationManager nManager) {
+ Log.d(TAG, "notifyScreenshotError");
Resources r = context.getResources();
// Clear all existing notification, compose the new notification and show it
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 456b5fa..1954af8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -23,6 +23,7 @@
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
+import android.util.Log;
public class TakeScreenshotService extends Service {
private static final String TAG = "TakeScreenshotService";
@@ -37,12 +38,15 @@
final Messenger callback = msg.replyTo;
if (mScreenshot == null) {
mScreenshot = new GlobalScreenshot(TakeScreenshotService.this);
+ Log.d(TAG, "Global screenshot initialized");
}
+ Log.d(TAG, "Global screenshot captured");
mScreenshot.takeScreenshot(new Runnable() {
@Override public void run() {
Message reply = Message.obtain(null, 1);
try {
callback.send(reply);
+ Log.d(TAG, "Global screenshot completed");
} catch (RemoteException e) {
}
}
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index 6a62809..18b4ec1 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -1889,7 +1889,7 @@
mHandler.post(new Runnable() {
public void run() {
try {
- ActivityManagerNative.getDefault().switchUser(0);
+ ActivityManagerNative.getDefault().switchUser(UserHandle.USER_OWNER);
((UserManager) mContext.getSystemService(Context.USER_SERVICE))
.removeUser(userHandle);
} catch (RemoteException re) {
diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java
index 88603dc..2a62c17 100644
--- a/services/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/java/com/android/server/accounts/AccountManagerService.java
@@ -21,6 +21,7 @@
import android.accounts.AccountAndUser;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
+import android.accounts.AccountManagerResponse;
import android.accounts.AuthenticatorDescription;
import android.accounts.GrantCredentialsPermissionActivity;
import android.accounts.IAccountAuthenticator;
@@ -526,6 +527,9 @@
}
if (account == null) throw new IllegalArgumentException("account is null");
checkAuthenticateAccountsPermission(account);
+ if (!canUserModifyAccounts(Binder.getCallingUid())) {
+ return false;
+ }
UserAccounts accounts = getUserAccountsForCaller();
// fails if the account already exists
@@ -679,6 +683,14 @@
checkManageAccountsPermission();
UserHandle user = Binder.getCallingUserHandle();
UserAccounts accounts = getUserAccountsForCaller();
+ if (!canUserModifyAccounts(Binder.getCallingUid())) {
+ try {
+ response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
+ "User cannot modify accounts");
+ } catch (RemoteException re) {
+ }
+ }
+
long identityToken = clearCallingIdentity();
cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
@@ -2312,6 +2324,17 @@
Manifest.permission.USE_CREDENTIALS);
}
+ private boolean canUserModifyAccounts(int callingUid) {
+ if (callingUid != android.os.Process.myUid()) {
+ Bundle restrictions = getUserManager().getUserRestrictions(
+ new UserHandle(UserHandle.getUserId(callingUid)));
+ if (!restrictions.getBoolean(UserManager.ALLOW_MODIFY_ACCOUNTS)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
public void updateAppPermission(Account account, String authTokenType, int uid, boolean value)
throws RemoteException {
final int callingUid = getCallingUid();
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 46d2cca..b480aa0 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -110,6 +110,7 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.UserManager;
import android.os.Environment.UserEnvironment;
import android.security.SystemKeyStore;
import android.util.DisplayMetrics;
@@ -5649,6 +5650,14 @@
null);
final int uid = Binder.getCallingUid();
+ if (!isUserAllowed(uid, UserManager.ALLOW_INSTALL_APPS)) {
+ try {
+ observer.packageInstalled("", PackageManager.INSTALL_FAILED_USER_RESTRICTED);
+ } catch (RemoteException re) {
+ }
+ return;
+ }
+
UserHandle user;
if ((flags&PackageManager.INSTALL_ALL_USERS) != 0) {
user = UserHandle.ALL;
@@ -5685,6 +5694,9 @@
PackageSetting pkgSetting;
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(uid);
+ if (!isUserAllowed(uid, UserManager.ALLOW_INSTALL_APPS)) {
+ return PackageManager.INSTALL_FAILED_USER_RESTRICTED;
+ }
long callingId = Binder.clearCallingIdentity();
try {
@@ -5715,7 +5727,19 @@
return PackageManager.INSTALL_SUCCEEDED;
}
-
+
+ private boolean isUserAllowed(int callingUid, String restrictionKey) {
+ if (callingUid != android.os.Process.myUid()) {
+ Bundle restrictions = sUserManager.getUserRestrictions(
+ UserHandle.getUserId(callingUid));
+ if (!restrictions.getBoolean(UserManager.ALLOW_INSTALL_APPS)) {
+ Log.w(TAG, "User does not have permission to: " + restrictionKey);
+ return false;
+ }
+ }
+ return true;
+ }
+
@Override
public void verifyPendingInstall(int id, int verificationCode) throws RemoteException {
mContext.enforceCallingOrSelfPermission(
@@ -8071,6 +8095,14 @@
android.Manifest.permission.DELETE_PACKAGES, null);
// Queue up an async operation since the package deletion may take a little while.
final int uid = Binder.getCallingUid();
+ if (!isUserAllowed(uid, UserManager.ALLOW_UNINSTALL_APPS)) {
+ try {
+ observer.packageDeleted(packageName, PackageManager.DELETE_FAILED_USER_RESTRICTED);
+ } catch (RemoteException re) {
+ }
+ return;
+ }
+
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index dbfe34d..5760dcd 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -30,6 +30,7 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
@@ -81,6 +82,7 @@
private static final String ATTR_USER_VERSION = "version";
private static final String TAG_USERS = "users";
private static final String TAG_USER = "user";
+ private static final String TAG_RESTRICTIONS = "restrictions";
private static final String USER_INFO_DIR = "system" + File.separator + "users";
private static final String USER_LIST_FILENAME = "userlist.xml";
@@ -104,6 +106,7 @@
private final File mBaseUserPath;
private final SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
+ private final SparseArray<Bundle> mUserRestrictions = new SparseArray<Bundle>();
/**
* Set of user IDs being actively removed. Removed IDs linger in this set
@@ -343,6 +346,26 @@
}
}
+ @Override
+ public Bundle getUserRestrictions(int userId) {
+ // checkManageUsersPermission("getUserRestrictions");
+
+ synchronized (mPackagesLock) {
+ Bundle restrictions = mUserRestrictions.get(userId);
+ return restrictions != null ? restrictions : Bundle.EMPTY;
+ }
+ }
+
+ @Override
+ public void setUserRestrictions(Bundle restrictions, int userId) {
+ checkManageUsersPermission("setUserRestrictions");
+
+ synchronized (mPackagesLock) {
+ mUserRestrictions.get(userId).putAll(restrictions);
+ writeUserLocked(mUsers.get(userId));
+ }
+ }
+
/**
* Check if we've hit the limit of how many users can be created.
*/
@@ -454,7 +477,7 @@
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
String id = parser.getAttributeValue(null, ATTR_ID);
- UserInfo user = readUser(Integer.parseInt(id));
+ UserInfo user = readUserLocked(Integer.parseInt(id));
if (user != null) {
mUsers.put(user.id, user);
@@ -568,6 +591,15 @@
serializer.text(userInfo.name);
serializer.endTag(null, TAG_NAME);
+ Bundle restrictions = mUserRestrictions.get(userInfo.id);
+ if (restrictions != null) {
+ serializer.startTag(null, TAG_RESTRICTIONS);
+ writeBoolean(serializer, restrictions, UserManager.ALLOW_CONFIG_WIFI);
+ writeBoolean(serializer, restrictions, UserManager.ALLOW_MODIFY_ACCOUNTS);
+ writeBoolean(serializer, restrictions, UserManager.ALLOW_INSTALL_APPS);
+ writeBoolean(serializer, restrictions, UserManager.ALLOW_UNINSTALL_APPS);
+ serializer.endTag(null, TAG_RESTRICTIONS);
+ }
serializer.endTag(null, TAG_USER);
serializer.endDocument();
@@ -620,7 +652,7 @@
}
}
- private UserInfo readUser(int id) {
+ private UserInfo readUserLocked(int id) {
int flags = 0;
int serialNumber = id;
String name = null;
@@ -628,6 +660,8 @@
long creationTime = 0L;
long lastLoggedInTime = 0L;
boolean partial = false;
+ Bundle restrictions = new Bundle();
+ initRestrictionsToDefaults(restrictions);
FileInputStream fis = null;
try {
@@ -663,13 +697,23 @@
partial = true;
}
- while ((type = parser.next()) != XmlPullParser.START_TAG
- && type != XmlPullParser.END_DOCUMENT) {
- }
- if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_NAME)) {
- type = parser.next();
- if (type == XmlPullParser.TEXT) {
- name = parser.getText();
+ int outerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ String tag = parser.getName();
+ if (TAG_NAME.equals(tag)) {
+ type = parser.next();
+ if (type == XmlPullParser.TEXT) {
+ name = parser.getText();
+ }
+ } else if (TAG_RESTRICTIONS.equals(tag)) {
+ readBoolean(parser, restrictions, UserManager.ALLOW_CONFIG_WIFI);
+ readBoolean(parser, restrictions, UserManager.ALLOW_MODIFY_ACCOUNTS);
+ readBoolean(parser, restrictions, UserManager.ALLOW_INSTALL_APPS);
+ readBoolean(parser, restrictions, UserManager.ALLOW_UNINSTALL_APPS);
}
}
}
@@ -679,6 +723,7 @@
userInfo.creationTime = creationTime;
userInfo.lastLoggedInTime = lastLoggedInTime;
userInfo.partial = partial;
+ mUserRestrictions.append(id, restrictions);
return userInfo;
} catch (IOException ioe) {
@@ -694,6 +739,27 @@
return null;
}
+ private void readBoolean(XmlPullParser parser, Bundle restrictions,
+ String restrictionKey) {
+ String value = parser.getAttributeValue(null, restrictionKey);
+ restrictions.putBoolean(restrictionKey, value == null ? true : Boolean.parseBoolean(value));
+ }
+
+ private void writeBoolean(XmlSerializer xml, Bundle restrictions, String restrictionKey)
+ throws IOException {
+ if (restrictions.containsKey(restrictionKey)) {
+ xml.attribute(null, restrictionKey,
+ Boolean.toString(restrictions.getBoolean(restrictionKey)));
+ }
+ }
+
+ private void initRestrictionsToDefaults(Bundle restrictions) {
+ restrictions.putBoolean(UserManager.ALLOW_CONFIG_WIFI, true);
+ restrictions.putBoolean(UserManager.ALLOW_MODIFY_ACCOUNTS, true);
+ restrictions.putBoolean(UserManager.ALLOW_INSTALL_APPS, true);
+ restrictions.putBoolean(UserManager.ALLOW_UNINSTALL_APPS, true);
+ }
+
private int readIntAttribute(XmlPullParser parser, String attr, int defaultValue) {
String valueString = parser.getAttributeValue(null, attr);
if (valueString == null) return defaultValue;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 1758d93..c4911a0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -21,8 +21,10 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.UserInfo;
+import android.os.Bundle;
import android.os.Debug;
import android.os.Environment;
+import android.os.UserHandle;
import android.os.UserManager;
import android.test.AndroidTestCase;
@@ -140,6 +142,20 @@
}
}
+ public void testRestrictions() {
+ List<UserInfo> users = mUserManager.getUsers();
+ if (users.size() > 1) {
+ Bundle restrictions = new Bundle();
+ restrictions.putBoolean(UserManager.ALLOW_INSTALL_APPS, false);
+ restrictions.putBoolean(UserManager.ALLOW_CONFIG_WIFI, true);
+ mUserManager.setUserRestrictions(restrictions, new UserHandle(users.get(1).id));
+ Bundle stored = mUserManager.getUserRestrictions(new UserHandle(users.get(1).id));
+ assertEquals(stored.getBoolean(UserManager.ALLOW_CONFIG_WIFI), true);
+ assertEquals(stored.getBoolean(UserManager.ALLOW_UNINSTALL_APPS), true);
+ assertEquals(stored.getBoolean(UserManager.ALLOW_INSTALL_APPS), false);
+ }
+ }
+
private void removeUser(int userId) {
synchronized (mUserLock) {
mUserManager.removeUser(userId);
@@ -151,4 +167,5 @@
}
}
}
+
}