Merge "Destroy docked divider surface when it's hidden."
diff --git a/api/current.txt b/api/current.txt
index 0263927..0774a9a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5776,6 +5776,7 @@
method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
method public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName);
method public android.os.Bundle getUserRestrictions(android.content.ComponentName);
+ method public java.lang.String getWifiMacAddress();
method public boolean hasCaCertInstalled(android.content.ComponentName, byte[]);
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
method public boolean installCaCert(android.content.ComponentName, byte[]);
diff --git a/api/system-current.txt b/api/system-current.txt
index 621a282..fbc60cd 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5906,6 +5906,7 @@
method public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
method public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName);
method public android.os.Bundle getUserRestrictions(android.content.ComponentName);
+ method public java.lang.String getWifiMacAddress();
method public boolean hasCaCertInstalled(android.content.ComponentName, byte[]);
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
method public boolean installCaCert(android.content.ComponentName, byte[]);
@@ -33054,6 +33055,7 @@
public class TelecomManager {
method public void acceptRingingCall();
+ method public void acceptRingingCall(int);
method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
method public void addNewUnknownCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
method public void cancelMissedCallsNotification();
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 303078b..19d9fc2 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -752,7 +752,12 @@
int createMode = data.readInt();
boolean toTop = data.readInt() != 0;
boolean animate = data.readInt() != 0;
- moveTaskToDockedStack(taskId, createMode, toTop, animate);
+ Rect bounds = null;
+ boolean hasBounds = data.readInt() != 0;
+ if (hasBounds) {
+ bounds = Rect.CREATOR.createFromParcel(data);
+ }
+ moveTaskToDockedStack(taskId, createMode, toTop, animate, bounds);
reply.writeNoException();
return true;
}
@@ -3578,8 +3583,8 @@
reply.recycle();
}
@Override
- public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate)
- throws RemoteException
+ public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate,
+ Rect initialBounds) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -3588,6 +3593,12 @@
data.writeInt(createMode);
data.writeInt(toTop ? 1 : 0);
data.writeInt(animate ? 1 : 0);
+ if (initialBounds != null) {
+ data.writeInt(1);
+ initialBounds.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
mRemote.transact(MOVE_TASK_TO_DOCKED_STACK_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 6594990..09c6c0b 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -142,8 +142,8 @@
public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) throws RemoteException;
public void moveTaskBackwards(int task) throws RemoteException;
public void moveTaskToStack(int taskId, int stackId, boolean toTop) throws RemoteException;
- public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate)
- throws RemoteException;
+ public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate,
+ Rect initialBounds) throws RemoteException;
public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) throws RemoteException;
public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode) throws RemoteException;
public void positionTaskInStack(int taskId, int stackId, int position) throws RemoteException;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 1c65c94..471750e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -89,6 +89,10 @@
private final Context mContext;
private final IDevicePolicyManager mService;
+ // TODO Use it everywhere.
+ private static final String REMOTE_EXCEPTION_MESSAGE =
+ "Failed to talk with device policy manager service";
+
private DevicePolicyManager(Context context) {
this(context, IDevicePolicyManager.Stub.asInterface(
ServiceManager.getService(Context.DEVICE_POLICY_SERVICE)));
@@ -686,7 +690,7 @@
* extra field. This will invoke a UI to bring the user through adding the profile owner admin
* to remotely control restrictions on the user.
*
- * <p>The intent must be invoked via {@link Activity#startActivityForResult()} to receive the
+ * <p>The intent must be invoked via {@link Activity#startActivityForResult} to receive the
* result of whether or not the user approved the action. If approved, the result will
* be {@link Activity#RESULT_OK} and the component will be set as an active admin as well
* as a profile owner.
@@ -2765,37 +2769,94 @@
* the setup process.
* @param packageName the package name of the app, to compare with the registered device owner
* app, if any.
- * @return whether or not the package is registered as the device owner app. Note this method
- * does *not* check weather the device owner is actually running on the current user.
+ * @return whether or not the package is registered as the device owner app.
*/
public boolean isDeviceOwnerApp(String packageName) {
+ return isDeviceOwnerAppOnCallingUser(packageName);
+ }
+
+ /**
+ * @return true if a package is registered as device owner, only when it's running on the
+ * calling user.
+ *
+ * <p>Same as {@link #isDeviceOwnerApp}, but bundled code should use it for clarity.
+ * @hide
+ */
+ public boolean isDeviceOwnerAppOnCallingUser(String packageName) {
+ return isDeviceOwnerAppOnAnyUserInner(packageName, /* callingUserOnly =*/ true);
+ }
+
+ /**
+ * @return true if a package is registered as device owner, even if it's running on a different
+ * user.
+ *
+ * <p>Requires the MANAGE_USERS permission.
+ *
+ * @hide
+ */
+ public boolean isDeviceOwnerAppOnAnyUser(String packageName) {
+ return isDeviceOwnerAppOnAnyUserInner(packageName, /* callingUserOnly =*/ false);
+ }
+
+ /**
+ * @return device owner component name, only when it's running on the calling user.
+ *
+ * @hide
+ */
+ public ComponentName getDeviceOwnerComponentOnCallingUser() {
+ return getDeviceOwnerComponentInner(/* callingUserOnly =*/ true);
+ }
+
+ /**
+ * @return device owner component name, even if it's running on a different user.
+ *
+ * <p>Requires the MANAGE_USERS permission.
+ *
+ * @hide
+ */
+ public ComponentName getDeviceOwnerComponentOnAnyUser() {
+ return getDeviceOwnerComponentInner(/* callingUserOnly =*/ false);
+ }
+
+ private boolean isDeviceOwnerAppOnAnyUserInner(String packageName, boolean callingUserOnly) {
if (packageName == null) {
return false;
}
- final ComponentName deviceOwner = getDeviceOwnerComponent();
+ final ComponentName deviceOwner = getDeviceOwnerComponentInner(callingUserOnly);
if (deviceOwner == null) {
return false;
}
return packageName.equals(deviceOwner.getPackageName());
}
- /**
- * @hide
- * Redirect to isDeviceOwnerApp.
- */
- public boolean isDeviceOwner(String packageName) {
- return isDeviceOwnerApp(packageName);
+ private ComponentName getDeviceOwnerComponentInner(boolean callingUserOnly) {
+ if (mService != null) {
+ try {
+ return mService.getDeviceOwnerComponent(callingUserOnly);
+ } catch (RemoteException re) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE);
+ }
+ }
+ return null;
}
/**
- * Check whether a given component is registered as a device owner.
- * Note this method does *not* check weather the device owner is actually running on the current
- * user.
+ * @return ID of the user who runs device owner, or {@link UserHandle#USER_NULL} if there's
+ * no device owner.
+ *
+ * <p>Requires the MANAGE_USERS permission.
*
* @hide
*/
- public boolean isDeviceOwner(ComponentName who) {
- return (who != null) && who.equals(getDeviceOwner());
+ public int getDeviceOwnerUserId() {
+ if (mService != null) {
+ try {
+ return mService.getDeviceOwnerUserId();
+ } catch (RemoteException re) {
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE);
+ }
+ }
+ return UserHandle.USER_NULL;
}
/**
@@ -2818,46 +2879,43 @@
}
/**
- * Returns the device owner package name. Note this method will still return the device owner
- * package name even if it's running on a different user.
+ * Returns the device owner package name, only if it's running on the calling user.
+ *
+ * <p>Bundled components should use {@code getDeviceOwnerComponentOnCallingUser()} for clarity.
*
* @hide
*/
@SystemApi
public String getDeviceOwner() {
- final ComponentName componentName = getDeviceOwnerComponent();
- return componentName == null ? null : componentName.getPackageName();
+ final ComponentName name = getDeviceOwnerComponentOnCallingUser();
+ return name != null ? name.getPackageName() : null;
}
/**
- * Returns the device owner name. Note this method will still return the device owner
- * name even if it's running on a different user.
+ * @return true if the device is managed by any device owner.
+ *
+ * <p>Requires the MANAGE_USERS permission.
*
* @hide
*/
- public String getDeviceOwnerName() {
+ public boolean isDeviceManaged() {
+ return getDeviceOwnerComponentOnAnyUser() != null;
+ }
+
+ /**
+ * Returns the device owner name. Note this method *will* return the device owner
+ * name when it's running on a different user.
+ *
+ * <p>Requires the MANAGE_USERS permission.
+ *
+ * @hide
+ */
+ public String getDeviceOwnerNameOnAnyUser() {
if (mService != null) {
try {
return mService.getDeviceOwnerName();
} catch (RemoteException re) {
- Log.w(TAG, "Failed to get device owner");
- }
- }
- return null;
- }
-
- /**
- * Returns the device owner component name. Note this method will still return the device owner
- * component name even if it's running on a different user.
- *
- * @hide
- */
- public ComponentName getDeviceOwnerComponent() {
- if (mService != null) {
- try {
- return mService.getDeviceOwner();
- } catch (RemoteException re) {
- Log.w(TAG, "Failed to get device owner");
+ Log.w(TAG, REMOTE_EXCEPTION_MESSAGE);
}
}
return null;
@@ -3130,7 +3188,7 @@
/**
* @hide
- * @param user The user for whom to fetch the profile owner name, if any.
+ * @param userId The user for whom to fetch the profile owner name, if any.
* @return the human readable name of the organisation associated with this profile owner or
* null if one is not set.
* @throws IllegalArgumentException if the userId is invalid.
@@ -4591,4 +4649,21 @@
return false;
}
}
+
+ /**
+ * Called by device owner to get the MAC address of the Wi-Fi device.
+ *
+ * @return the MAC address of the Wi-Fi device, or null when the information is not
+ * available. (For example, Wi-Fi hasn't been enabled, or the device doesn't support Wi-Fi.)
+ *
+ * <p>The address will be in the {@code XX:XX:XX:XX:XX:XX} format.
+ */
+ public String getWifiMacAddress() {
+ try {
+ return mService.getWifiMacAddress();
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed talking with device policy service", re);
+ return null;
+ }
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index fc7c2b3..6b4567c 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -114,9 +114,10 @@
void reportSuccessfulPasswordAttempt(int userHandle);
boolean setDeviceOwner(in ComponentName who, String ownerName, int userId);
- ComponentName getDeviceOwner();
+ ComponentName getDeviceOwnerComponent(boolean callingUserOnly);
String getDeviceOwnerName();
void clearDeviceOwner(String packageName);
+ int getDeviceOwnerUserId();
boolean setProfileOwner(in ComponentName who, String ownerName, int userHandle);
ComponentName getProfileOwner(int userHandle);
@@ -235,4 +236,5 @@
List<String> getKeepUninstalledPackages(in ComponentName admin);
boolean isManagedProfile(in ComponentName admin);
boolean isSystemOnlyUser(in ComponentName admin);
+ String getWifiMacAddress();
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index d1531074..67e170f 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -336,8 +336,8 @@
}
@Override
- public void onResume() {
- super.onResume();
+ public void onStart() {
+ super.onStart();
if (mState != STATE_INITIALIZING && mCurrentPrinter != null) {
mPrinterRegistry.setTrackedPrinter(mCurrentPrinter.getId());
}
@@ -379,10 +379,15 @@
}
}
+ super.onPause();
+ }
+
+ @Override
+ protected void onStop() {
mPrinterAvailabilityDetector.cancel();
mPrinterRegistry.setTrackedPrinter(null);
- super.onPause();
+ super.onStop();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
index f5f6acf..4cb8a3c 100644
--- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
@@ -16,6 +16,7 @@
package com.android.systemui;
+import android.graphics.Rect;
import android.view.Display;
import android.view.View;
@@ -31,7 +32,7 @@
/**
* Docks the top-most task and opens recents.
*/
- void dockTopTask(boolean draggingInRecents);
+ void dockTopTask(boolean draggingInRecents, Rect initialBounds);
/**
* Called during a drag-from-navbar-in gesture.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index 47189b0..6d8b476 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -111,7 +111,9 @@
private void handleRefreshState() {
mIsIconVisible = mSecurityController.isVpnEnabled();
- if (mSecurityController.hasDeviceOwner()) {
+ // If the device has device owner, show "Device may be monitored", but --
+ // TODO See b/25779452 -- device owner doesn't actually have monitoring power.
+ if (mSecurityController.isDeviceManaged()) {
mFooterTextId = R.string.device_owned_footer;
mIsVisible = true;
} else {
@@ -156,6 +158,8 @@
private String getMessage(String deviceOwner, String profileOwner, String primaryVpn,
String profileVpn, boolean primaryUserIsManaged) {
+ // Show a special warning when the device has device owner, but --
+ // TODO See b/25779452 -- device owner doesn't actually have monitoring power.
if (deviceOwner != null) {
if (primaryVpn != null) {
return mContext.getString(R.string.monitoring_description_vpn_app_device_owned,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
index 69e9359..b36b95a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
+++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
@@ -24,7 +24,8 @@
oneway interface IRecentsNonSystemUserCallbacks {
void preloadRecents();
void cancelPreloadingRecents();
- void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents, boolean animate);
+ void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents, boolean animate,
+ boolean reloadTasks);
void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
void toggleRecents();
void onConfigurationChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 186cace..c98ecb5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -21,6 +21,7 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -214,7 +215,8 @@
int currentUser = sSystemServicesProxy.getCurrentUser();
if (sSystemServicesProxy.isSystemUser(currentUser)) {
- mImpl.showRecents(triggeredFromAltTab, false /* draggingInRecents */, true /* animate */);
+ mImpl.showRecents(triggeredFromAltTab, false /* draggingInRecents */,
+ true /* animate */, false /* reloadTasks */);
} else {
if (mSystemUserCallbacks != null) {
IRecentsNonSystemUserCallbacks callbacks =
@@ -222,7 +224,7 @@
if (callbacks != null) {
try {
callbacks.showRecents(triggeredFromAltTab, false /* draggingInRecents */,
- true /* animate */);
+ true /* animate */, false /* reloadTasks */);
} catch (RemoteException e) {
Log.e(TAG, "Callback failed", e);
}
@@ -363,8 +365,8 @@
}
@Override
- public void dockTopTask(boolean draggingInRecents) {
- mImpl.dockTopTask(draggingInRecents);
+ public void dockTopTask(boolean draggingInRecents, Rect initialBounds) {
+ mImpl.dockTopTask(draggingInRecents, initialBounds);
if (draggingInRecents) {
mDraggingInRecentsCurrentUser = sSystemServicesProxy.getCurrentUser();
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 92978f2..16b1592 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -448,6 +448,7 @@
launchState.launchedToTaskId = -1;
launchState.launchedWithAltTab = false;
launchState.launchedHasConfigurationChanged = false;
+ launchState.launchedViaDragGesture = false;
MetricsLogger.hidden(this, MetricsLogger.OVERVIEW_ACTIVITY);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index a1e5118..8dd9e47 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -46,6 +46,7 @@
launchedReuseTaskStackViews = false;
// Set this flag to indicate that the configuration has changed since Recents last launched
launchedHasConfigurationChanged = true;
+ launchedViaDragGesture = false;
}
/** Returns whether the status bar scrim should be animated when shown for the first time. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index e0ff4cc..ee57863b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -144,6 +144,7 @@
boolean mBootCompleted;
boolean mCanReuseTaskStackViews = true;
boolean mDraggingInRecents;
+ boolean mReloadTasks;
// Task launching
Rect mSearchBarBounds = new Rect();
@@ -168,7 +169,8 @@
public void run() {
// When this fires, then the user has not released alt-tab for at least
// FAST_ALT_TAB_DELAY_MS milliseconds
- showRecents(mTriggeredFromAltTab, false /* draggingInRecents */, true /* animate */);
+ showRecents(mTriggeredFromAltTab, false /* draggingInRecents */, true /* animate */,
+ false /* reloadTasks */);
}
});
@@ -252,9 +254,10 @@
@Override
public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents,
- boolean animate) {
+ boolean animate, boolean reloadTasks) {
mTriggeredFromAltTab = triggeredFromAltTab;
mDraggingInRecents = draggingInRecents;
+ mReloadTasks = reloadTasks;
if (mFastAltTabTrigger.hasTriggered()) {
// We are calling this from the doze trigger, so just fall through to show Recents
mFastAltTabTrigger.resetTrigger();
@@ -537,13 +540,14 @@
showRelativeAffiliatedTask(false);
}
- public void dockTopTask(boolean draggingInRecents) {
+ public void dockTopTask(boolean draggingInRecents, Rect initialBounds) {
SystemServicesProxy ssp = Recents.getSystemServices();
ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
if (topTask != null && !SystemServicesProxy.isHomeStack(topTask.stackId)) {
ssp.moveTaskToDockedStack(topTask.id,
- ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT);
- showRecents(false /* triggeredFromAltTab */, draggingInRecents, false /* animate */);
+ ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, initialBounds);
+ showRecents(false /* triggeredFromAltTab */, draggingInRecents, false /* animate */,
+ true /* reloadTasks*/);
}
}
@@ -800,12 +804,13 @@
reloadHeaderBarLayout(false /* tryAndBindSearchWidget */);
// In the case where alt-tab is triggered, we never get a preloadRecents() call, so we
- // should always preload the tasks now
- if (mTriggeredFromAltTab ||sInstanceLoadPlan == null) {
+ // should always preload the tasks now. If we are dragging in recents, reload them as
+ // the stacks might have changed.
+ if (mReloadTasks || mTriggeredFromAltTab ||sInstanceLoadPlan == null) {
// Create a new load plan if preloadRecents() was never triggered
sInstanceLoadPlan = loader.createLoadPlan(mContext);
}
- if (mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
+ if (mReloadTasks || mTriggeredFromAltTab || !sInstanceLoadPlan.hasTasks()) {
loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
}
TaskStack stack = sInstanceLoadPlan.getTaskStack();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 5026c790..de40a37 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -312,11 +312,12 @@
}
/** Docks an already resumed task to the side of the screen. */
- public void moveTaskToDockedStack(int taskId, int createMode) {
+ public void moveTaskToDockedStack(int taskId, int createMode, Rect initialBounds) {
if (mIam == null) return;
try {
- mIam.moveTaskToDockedStack(taskId, createMode, true /* onTop */, false /* animate */);
+ mIam.moveTaskToDockedStack(taskId, createMode, true /* onTop */, false /* animate */,
+ initialBounds);
} catch (RemoteException e) {
e.printStackTrace();
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index ff3aef9..d7eb099 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -190,6 +190,9 @@
};
}
try {
+ synchronized (this) {
+ mAppTransitionAnimationSpecs = SPECS_WAITING;
+ }
WindowManagerGlobal.getWindowManagerService()
.overridePendingAppTransitionMultiThumbFuture(transitionFuture,
callback, true /* scaleUp */);
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 3e317ff..f045814 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -301,22 +301,26 @@
return mStartPosition + touchY - mStartY;
}
- public void resizeStack(int position) {
- mTmpRect.set(0, 0, mDisplayWidth, mDisplayHeight);
- switch (mDockSide) {
+ public void calculateBoundsForPosition(int position, int dockSide, Rect outRect) {
+ outRect.set(0, 0, mDisplayWidth, mDisplayHeight);
+ switch (dockSide) {
case WindowManager.DOCKED_LEFT:
- mTmpRect.right = position;
+ outRect.right = position;
break;
case WindowManager.DOCKED_TOP:
- mTmpRect.bottom = position;
+ outRect.bottom = position;
break;
case WindowManager.DOCKED_RIGHT:
- mTmpRect.left = position + mDividerWindowWidth - 2 * mDividerInsets;
+ outRect.left = position + mDividerWindowWidth - 2 * mDividerInsets;
break;
case WindowManager.DOCKED_BOTTOM:
- mTmpRect.top = position + mDividerWindowWidth - 2 * mDividerInsets;
+ outRect.top = position + mDividerWindowWidth - 2 * mDividerInsets;
break;
}
+ }
+
+ public void resizeStack(int position) {
+ calculateBoundsForPosition(position, mDockSide, mTmpRect);
if (mTmpRect.equals(mLastResizeRect)) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index 742be02..d91bfb9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Rect;
import android.os.SystemProperties;
import android.view.GestureDetector;
import android.view.MotionEvent;
@@ -170,7 +171,18 @@
if (touchSlopExceeded && mDivider.getView().getWindowManagerProxy().getDockSide()
== DOCKED_INVALID) {
mDragMode = calculateDragMode();
- mRecentsComponent.dockTopTask(mDragMode == DRAG_MODE_RECENTS);
+ Rect initialBounds = null;
+ if (mDragMode == DRAG_MODE_DIVIDER) {
+ initialBounds = new Rect();
+ mDivider.getView().calculateBoundsForPosition(mIsVertical
+ ? (int) event.getRawX()
+ : (int) event.getRawY(),
+ mDivider.getView().isHorizontalDivision()
+ ? DOCKED_TOP
+ : DOCKED_LEFT,
+ initialBounds);
+ }
+ mRecentsComponent.dockTopTask(mDragMode == DRAG_MODE_RECENTS, initialBounds);
if (mDragMode == DRAG_MODE_DIVIDER) {
mDivider.getView().startDragging();
}
@@ -193,12 +205,12 @@
mVelocityTracker.computeCurrentVelocity(1000);
if (mDockWindowTouchSlopExceeded) {
if (mDragMode == DRAG_MODE_DIVIDER) {
- mDivider.getView().stopDragging(!mIsVertical
- ? (int) event.getRawY()
- : (int) event.getRawX(),
- !mIsVertical
- ? mVelocityTracker.getYVelocity()
- : mVelocityTracker.getXVelocity());
+ mDivider.getView().stopDragging(mIsVertical
+ ? (int) event.getRawX()
+ : (int) event.getRawY(),
+ mIsVertical
+ ? mVelocityTracker.getXVelocity()
+ : mVelocityTracker.getYVelocity());
} else if (mDragMode == DRAG_MODE_RECENTS) {
mRecentsComponent.onDraggingInRecentsEnded(mVelocityTracker.getYVelocity());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 0f182ac..6d8e650 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1134,7 +1134,7 @@
@Override
public boolean onLongClick(View v) {
if (mRecents != null) {
- mRecents.dockTopTask(false /* draggingInRecents */);
+ mRecents.dockTopTask(false /* draggingInRecents */, null /* initialBounds */);
return true;
}
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 4d268ce..ba284c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -52,6 +52,7 @@
private boolean mSupportsLongpress = true;
private AudioManager mAudioManager;
private boolean mGestureAborted;
+ private boolean mLongClicked;
private final Runnable mCheckLongPress = new Runnable() {
public void run() {
@@ -60,9 +61,11 @@
if (isLongClickable()) {
// Just an old-fashioned ImageView
performLongClick();
+ mLongClicked = true;
} else if (mSupportsLongpress) {
sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
+ mLongClicked = true;
}
}
}
@@ -155,6 +158,7 @@
switch (action) {
case MotionEvent.ACTION_DOWN:
mDownTime = SystemClock.uptimeMillis();
+ mLongClicked = false;
setPressed(true);
if (mCode != 0) {
sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);
@@ -181,7 +185,7 @@
removeCallbacks(mCheckLongPress);
break;
case MotionEvent.ACTION_UP:
- final boolean doIt = isPressed();
+ final boolean doIt = isPressed() && !mLongClicked;
setPressed(false);
if (mCode != 0) {
if (doIt) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
index f06e5d3..a22f988 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
@@ -16,8 +16,8 @@
package com.android.systemui.statusbar.policy;
public interface SecurityController {
-
- boolean hasDeviceOwner();
+ /** Whether the device has device owner, even if not on this user. */
+ boolean isDeviceManaged();
boolean hasProfileOwner();
String getDeviceOwnerName();
String getProfileOwnerName();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 88f028f..6ddd7a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -102,13 +102,13 @@
}
@Override
- public boolean hasDeviceOwner() {
- return !TextUtils.isEmpty(mDevicePolicyManager.getDeviceOwner());
+ public boolean isDeviceManaged() {
+ return mDevicePolicyManager.isDeviceManaged();
}
@Override
public String getDeviceOwnerName() {
- return mDevicePolicyManager.getDeviceOwnerName();
+ return mDevicePolicyManager.getDeviceOwnerNameOnAnyUser();
}
@Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 94a908c..9650088 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4317,7 +4317,8 @@
if (launchStackId != INVALID_STACK_ID) {
if (launchStackId == DOCKED_STACK_ID && bOptions != null) {
ActivityOptions activityOptions = new ActivityOptions(bOptions);
- mWindowManager.setDockedStackCreateMode(activityOptions.getDockCreateMode());
+ mWindowManager.setDockedStackCreateState(activityOptions.getDockCreateMode(),
+ null /* initialBounds */);
}
if (task.stack.mStackId != launchStackId) {
mStackSupervisor.moveTaskToStackLocked(
@@ -9280,9 +9281,12 @@
* {@link android.app.ActivityManager#DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT}
* @param toTop If the task and stack should be moved to the top.
* @param animate Whether we should play an animation for the moving the task
+ * @param initialBounds If the docked stack gets created, it will use these bounds for the
+ * docked stack. Pass {@code null} to use default bounds.
*/
@Override
- public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate) {
+ public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate,
+ Rect initialBounds) {
enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
"moveTaskToDockedStack()");
synchronized (this) {
@@ -9290,10 +9294,9 @@
try {
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToDockedStack: moving task=" + taskId
+ " to createMode=" + createMode + " toTop=" + toTop);
- mWindowManager.setDockedStackCreateMode(createMode);
- mStackSupervisor.moveTaskToStackLocked(
- taskId, DOCKED_STACK_ID, toTop, !FORCE_FOCUS, "moveTaskToDockedStack",
- animate);
+ mWindowManager.setDockedStackCreateState(createMode, initialBounds);
+ mStackSupervisor.moveTaskToStackLocked(taskId, DOCKED_STACK_ID, toTop, !FORCE_FOCUS,
+ "moveTaskToDockedStack", animate);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index cf09b84..5d1906c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -866,10 +866,11 @@
mAppOps.checkPackage(callingUid, callerPackageName);
}
- // Check whether the caller is device owner
+ // Check whether the caller is device owner, in which case we do it silently.
DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
Context.DEVICE_POLICY_SERVICE);
- boolean isDeviceOwner = (dpm != null) && dpm.isDeviceOwnerApp(callerPackageName);
+ boolean isDeviceOwner = (dpm != null) && dpm.isDeviceOwnerAppOnCallingUser(
+ callerPackageName);
final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
statusReceiver, packageName, isDeviceOwner, userId);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index b0e43a5..fa0aa37 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -228,7 +228,8 @@
final boolean isInstallerRoot = (installerUid == Process.ROOT_UID);
final boolean forcePermissionPrompt =
(params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0;
- mIsInstallerDeviceOwner = (dpm != null) && dpm.isDeviceOwnerApp(installerPackageName);
+ mIsInstallerDeviceOwner = (dpm != null) && dpm.isDeviceOwnerAppOnCallingUser(
+ installerPackageName);
if ((isPermissionGranted
|| isInstallerRoot
|| mIsInstallerDeviceOwner)
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 21a4206..4bc79cb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -13065,7 +13065,8 @@
ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
try {
if (dpm != null) {
- final ComponentName deviceOwnerComponentName = dpm.getDeviceOwner();
+ final ComponentName deviceOwnerComponentName = dpm.getDeviceOwnerComponent(
+ /* callingUserOnly =*/ false);
final String deviceOwnerPackageName = deviceOwnerComponentName == null ? null
: deviceOwnerComponentName.getPackageName();
// Does the package contains the device owner?
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 558ea58..ff829ff 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -513,7 +513,7 @@
DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
Context.DEVICE_POLICY_SERVICE);
// restricted profile can be created if there is no DO set and the admin user has no PO
- return dpm.getDeviceOwner() == null && dpm.getProfileOwnerAsUser(userId) == null;
+ return !dpm.isDeviceManaged() && dpm.getProfileOwnerAsUser(userId) == null;
}
/*
@@ -1599,7 +1599,7 @@
DevicePolicyManager devicePolicyManager = (DevicePolicyManager)
mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
if (devicePolicyManager == null
- || devicePolicyManager.getDeviceOwner() == null) {
+ || !devicePolicyManager.isDeviceManaged()) {
flags |= UserInfo.FLAG_ADMIN;
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 9a88c60..d394125 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -339,6 +339,9 @@
mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
mNextAppTransitionPackage = null;
mNextAppTransitionAnimationsSpecs.clear();
+ mNextAppTransitionAnimationsSpecsFuture = null;
+ mDefaultNextAppTransitionAnimationSpec = null;
+ mAnimationFinishedCallback = null;
}
void freeze() {
@@ -1327,17 +1330,10 @@
}
}
- private void clearAppTransitionState() {
- mNextAppTransitionPackage = null;
- mNextAppTransitionAnimationsSpecs.clear();
- mDefaultNextAppTransitionAnimationSpec = null;
- mAnimationFinishedCallback = null;
- }
-
void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
IRemoteCallback startedCallback) {
if (isTransitionSet()) {
- clearAppTransitionState();
+ clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
mNextAppTransitionPackage = packageName;
mNextAppTransitionEnter = enterAnim;
@@ -1352,7 +1348,7 @@
void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
int startHeight) {
if (isTransitionSet()) {
- clearAppTransitionState();
+ clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_SCALE_UP;
putDefaultNextAppTransitionCoordinates(startX, startY, startX + startWidth,
startY + startHeight, null);
@@ -1363,7 +1359,7 @@
void overridePendingAppTransitionClipReveal(int startX, int startY,
int startWidth, int startHeight) {
if (isTransitionSet()) {
- clearAppTransitionState();
+ clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CLIP_REVEAL;
putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight, null);
postAnimationCallback();
@@ -1373,7 +1369,7 @@
void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY,
IRemoteCallback startedCallback, boolean scaleUp) {
if (isTransitionSet()) {
- clearAppTransitionState();
+ clear();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN;
mNextAppTransitionScaleUp = scaleUp;
@@ -1388,7 +1384,7 @@
void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX, int startY,
int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp) {
if (isTransitionSet()) {
- clearAppTransitionState();
+ clear();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
mNextAppTransitionScaleUp = scaleUp;
@@ -1405,7 +1401,7 @@
IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback,
boolean scaleUp) {
if (isTransitionSet()) {
- clearAppTransitionState();
+ clear();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
mNextAppTransitionScaleUp = scaleUp;
@@ -1436,7 +1432,7 @@
IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback,
boolean scaleUp) {
if (isTransitionSet()) {
- clearAppTransitionState();
+ clear();
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
mNextAppTransitionAnimationsSpecsFuture = specsFuture;
@@ -1447,7 +1443,7 @@
void overrideInPlaceAppTransition(String packageName, int anim) {
if (isTransitionSet()) {
- clearAppTransitionState();
+ clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE;
mNextAppTransitionPackage = packageName;
mNextAppTransitionInPlace = anim;
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 804830e..09118c7 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -196,7 +196,8 @@
? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
: DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
mService.mActivityManager.moveTaskToDockedStack(
- mTask.mTaskId, createMode, true /*toTop*/, true /* animate */);
+ mTask.mTaskId, createMode, true /*toTop*/, true /* animate */,
+ null /* initialBounds */);
}
} catch(RemoteException e) {}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 04ab544..9d07fb0 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -387,7 +387,7 @@
if (dockedStack != null) {
dockedStack.getRawBounds(mTmpRect2);
}
- final boolean dockedOnTopOrLeft = WindowManagerService.sDockedStackCreateMode
+ final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
== DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
getStackDockedModeBounds(mTmpRect, bounds, mStackId, mTmpRect2,
mDisplayContent.mDividerControllerLocked.getContentWidth(),
@@ -451,7 +451,7 @@
* close to the side of the dock.
* @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen.
*/
- private static void getStackDockedModeBounds(
+ private void getStackDockedModeBounds(
Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int dockDividerWidth,
boolean dockOnTopOrLeft) {
final boolean dockedStack = stackId == DOCKED_STACK_ID;
@@ -459,6 +459,10 @@
outBounds.set(displayRect);
if (dockedStack) {
+ if (mService.mDockedStackCreateBounds != null) {
+ outBounds.set(mService.mDockedStackCreateBounds);
+ return;
+ }
// The initial bounds of the docked stack when it is created half the screen space and
// its bounds can be adjusted after that. The bounds of all other stacks are adjusted
// to occupy whatever screen space the docked stack isn't occupying.
@@ -505,7 +509,7 @@
final Rect bounds = new Rect();
mDisplayContent.getLogicalDisplayRect(bounds);
if (!fullscreen) {
- final boolean dockedOnTopOrLeft = WindowManagerService.sDockedStackCreateMode
+ final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
== DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
getStackDockedModeBounds(bounds, bounds, FULLSCREEN_WORKSPACE_STACK_ID, dockedBounds,
mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 685ec49..b80b895 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -483,7 +483,8 @@
private boolean mKeyguardWaitingForActivityDrawn;
- static int sDockedStackCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+ int mDockedStackCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+ Rect mDockedStackCreateBounds;
private final SparseIntArray mTmpTaskIds = new SparseIntArray();
@@ -4620,9 +4621,10 @@
return (stack != null && stack.isVisibleLocked());
}
- public void setDockedStackCreateMode(int mode) {
+ public void setDockedStackCreateState(int mode, Rect bounds) {
synchronized (mWindowMap) {
- sDockedStackCreateMode = mode;
+ mDockedStackCreateMode = mode;
+ mDockedStackCreateBounds = bounds;
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 8beee73..f80a611 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -71,8 +71,11 @@
import android.net.ConnectivityManager;
import android.net.ProxyInfo;
import android.net.Uri;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
@@ -1121,6 +1124,10 @@
return Looper.myLooper();
}
+ WifiManager getWifiManager() {
+ return mContext.getSystemService(WifiManager.class);
+ }
+
long binderClearCallingIdentity() {
return Binder.clearCallingIdentity();
}
@@ -1715,7 +1722,8 @@
}
}
- public DeviceAdminInfo findAdmin(ComponentName adminName, int userHandle) {
+ public DeviceAdminInfo findAdmin(ComponentName adminName, int userHandle,
+ boolean throwForMissiongPermission) {
if (!mHasFeature) {
return null;
}
@@ -1730,8 +1738,20 @@
throw new IllegalArgumentException("Unknown admin: " + adminName);
}
+ final ResolveInfo ri = infos.get(0);
+
+ if (!permission.BIND_DEVICE_ADMIN.equals(ri.activityInfo.permission)) {
+ final String message = "DeviceAdminReceiver " + adminName + " must be protected with"
+ + permission.BIND_DEVICE_ADMIN;
+ Slog.w(LOG_TAG, message);
+ if (throwForMissiongPermission &&
+ ri.activityInfo.applicationInfo.targetSdkVersion > Build.VERSION_CODES.M) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
try {
- return new DeviceAdminInfo(mContext, infos.get(0));
+ return new DeviceAdminInfo(mContext, ri);
} catch (XmlPullParserException e) {
Slog.w(LOG_TAG, "Bad device admin requested for user=" + userHandle + ": " + adminName,
e);
@@ -1922,7 +1942,8 @@
String name = parser.getAttributeValue(null, "name");
try {
DeviceAdminInfo dai = findAdmin(
- ComponentName.unflattenFromString(name), userHandle);
+ ComponentName.unflattenFromString(name), userHandle,
+ /* throwForMissionPermission= */ false);
if (VERBOSE_LOG
&& (UserHandle.getUserId(dai.getActivityInfo().applicationInfo.uid)
!= userHandle)) {
@@ -2045,9 +2066,12 @@
private void updateDeviceOwnerLocked() {
long ident = mInjector.binderClearCallingIdentity();
try {
- if (getDeviceOwner() != null) {
+ // TODO This is to prevent DO from getting "clear data"ed, but it should also check the
+ // user id and also protect all other DAs too.
+ final ComponentName deviceOwnerComponent = mOwners.getDeviceOwnerComponent();
+ if (deviceOwnerComponent != null) {
mInjector.getIActivityManager()
- .updateDeviceOwner(getDeviceOwner().getPackageName());
+ .updateDeviceOwner(deviceOwnerComponent.getPackageName());
}
} catch (RemoteException e) {
// Not gonna happen.
@@ -2248,6 +2272,7 @@
// Build and show a warning notification
int smallIconId;
String contentText;
+ // TODO Why does it use the DO name? The cert APIs are all for PO. b/25772443
final String ownerName = getDeviceOwnerName();
if (isManagedProfile(userHandle.getIdentifier())) {
contentText = mContext.getString(R.string.ssl_ca_cert_noti_by_administrator);
@@ -2309,7 +2334,8 @@
enforceCrossUserPermission(userHandle);
DevicePolicyData policy = getUserData(userHandle);
- DeviceAdminInfo info = findAdmin(adminReceiver, userHandle);
+ DeviceAdminInfo info = findAdmin(adminReceiver, userHandle,
+ /* throwForMissionPermission= */ true);
if (info == null) {
throw new IllegalArgumentException("Bad admin: " + adminReceiver);
}
@@ -4600,22 +4626,46 @@
}
@Override
- public ComponentName getDeviceOwner() {
+ public ComponentName getDeviceOwnerComponent(boolean callingUserOnly) {
if (!mHasFeature) {
return null;
}
+ if (!callingUserOnly) {
+ enforceManageUsers();
+ }
synchronized (this) {
+ if (!mOwners.hasDeviceOwner()) {
+ return null;
+ }
+ if (callingUserOnly && mInjector.userHandleGetCallingUserId() !=
+ mOwners.getDeviceOwnerUserId()) {
+ return null;
+ }
return mOwners.getDeviceOwnerComponent();
}
}
@Override
+ public int getDeviceOwnerUserId() {
+ if (!mHasFeature) {
+ return UserHandle.USER_NULL;
+ }
+ enforceManageUsers();
+ synchronized (this) {
+ return mOwners.hasDeviceOwner() ? mOwners.getDeviceOwnerUserId() : UserHandle.USER_NULL;
+ }
+ }
+
+ /**
+ * Returns the "name" of the device owner. It'll work for non-DO users too, but requires
+ * MANAGE_USERS.
+ */
+ @Override
public String getDeviceOwnerName() {
if (!mHasFeature) {
return null;
}
- // TODO: Do we really need it? getDeviceOwner() doesn't require it.
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
+ enforceManageUsers();
synchronized (this) {
if (!mOwners.hasDeviceOwner()) {
return null;
@@ -4630,7 +4680,7 @@
// Returns the active device owner or null if there is no device owner.
@VisibleForTesting
ActiveAdmin getDeviceOwnerAdminLocked() {
- ComponentName component = getDeviceOwner();
+ ComponentName component = mOwners.getDeviceOwnerComponent();
if (component == null) {
return null;
}
@@ -4659,16 +4709,20 @@
} catch (NameNotFoundException e) {
throw new SecurityException(e);
}
- if (!mOwners.hasDeviceOwner() || !getDeviceOwner().getPackageName().equals(packageName)
- || (mOwners.getDeviceOwnerUserId() != UserHandle.getUserId(callingUid))) {
- throw new SecurityException("clearDeviceOwner can only be called by the device owner");
- }
synchronized (this) {
+ if (!mOwners.hasDeviceOwner()
+ || !mOwners.getDeviceOwnerComponent().getPackageName().equals(packageName)
+ || (mOwners.getDeviceOwnerUserId() != UserHandle.getUserId(callingUid))) {
+ throw new SecurityException(
+ "clearDeviceOwner can only be called by the device owner");
+ }
+
final ActiveAdmin admin = getDeviceOwnerAdminLocked();
if (admin != null) {
admin.disableCamera = false;
admin.userRestrictions = null;
}
+
clearUserPoliciesLocked(new UserHandle(UserHandle.USER_SYSTEM));
mOwners.clearDeviceOwner();
@@ -4857,7 +4911,7 @@
if (!mHasFeature) {
return null;
}
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
+ enforceManageUsers();
ComponentName profileOwner = getProfileOwner(userHandle);
if (profileOwner == null) {
return null;
@@ -4987,13 +5041,20 @@
}
}
+ private void enforceManageUsers() {
+ final int callingUid = mInjector.binderGetCallingUid();
+ if (!(UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) || callingUid == 0)) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
+ }
+ }
+
private void enforceCrossUserPermission(int userHandle) {
if (userHandle < 0) {
throw new IllegalArgumentException("Invalid userId " + userHandle);
}
final int callingUid = mInjector.binderGetCallingUid();
if (userHandle == UserHandle.getUserId(callingUid)) return;
- if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
+ if (!(UserHandle.isSameApp(callingUid, Process.SYSTEM_UID) || callingUid == 0)) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "Must be system or have"
+ " INTERACT_ACROSS_USERS_FULL permission");
@@ -6619,8 +6680,9 @@
updateReceivedTime);
synchronized (this) {
- final String deviceOwnerPackage = getDeviceOwner() == null ? null :
- getDeviceOwner().getPackageName();
+ final String deviceOwnerPackage =
+ mOwners.hasDeviceOwner() ? mOwners.getDeviceOwnerComponent().getPackageName()
+ : null;
if (deviceOwnerPackage == null) {
return;
}
@@ -6831,6 +6893,25 @@
return true;
}
+ @Override
+ public String getWifiMacAddress() {
+ // Make sure caller has DO.
+ synchronized (this) {
+ getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ }
+
+ final long ident = mInjector.binderClearCallingIdentity();
+ try {
+ final WifiInfo wifiInfo = mInjector.getWifiManager().getConnectionInfo();
+ if (wifiInfo == null) {
+ return null;
+ }
+ return wifiInfo.hasRealMacAddress() ? wifiInfo.getMacAddress() : null;
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
+ }
+
/**
* Returns the target sdk version number that the given packageName was built for
* in the given user.
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index f37bb9eb..6a50a6e 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -343,9 +343,7 @@
public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer) {
synchronized (mLock) {
throwIfDestroyedLocked();
- if (mActiveServices.isEmpty()) {
- return;
- }
+
if (mPrinterDiscoverySession == null) {
// If we do not have a session, tell all service to create one.
mPrinterDiscoverySession = new PrinterDiscoverySessionMediator(mContext) {
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index c147bcc..eed326e 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -94,6 +94,14 @@
</intent-filter>
</receiver>
+ <receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$AdminNoPerm">
+ <meta-data android:name="android.app.device_admin"
+ android:resource="@xml/device_admin_sample" />
+ <intent-filter>
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ </intent-filter>
+ </receiver>
+
</application>
<instrumentation
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 0bd4896..565ef4b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -26,15 +26,21 @@
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.pm.PackageManager;
+import android.net.wifi.WifiInfo;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
import android.os.Bundle;
+import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
+import android.test.MoreAsserts;
import android.util.Pair;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -56,9 +62,9 @@
*
m FrameworksServicesTests &&
adb install \
- -r out/target/product/hammerhead/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
+ -r out/target/product/hammerhead/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
adb shell am instrument -e class com.android.server.devicepolicy.DevicePolicyManagerTest \
- -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
(mmma frameworks/base/services/tests/servicestests/ for non-ninja build)
*/
@@ -81,6 +87,7 @@
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID);
setUpPackageManagerForAdmin(admin3, DpmMockContext.CALLER_UID);
+ setUpPackageManagerForAdmin(adminNoPerm, DpmMockContext.CALLER_UID);
setUpUserManager();
}
@@ -334,6 +341,33 @@
/**
* Test for:
+ * {@link DevicePolicyManager#setActiveAdmin} when the admin isn't protected with
+ * BIND_DEVICE_ADMIN.
+ */
+ public void testSetActiveAdmin_permissionCheck() throws Exception {
+ // 1. Make sure the caller has proper permissions.
+ mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+
+ try {
+ dpm.setActiveAdmin(adminNoPerm, /* replace =*/ false);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ assertTrue(expected.getMessage().contains(permission.BIND_DEVICE_ADMIN));
+ }
+ assertFalse(dpm.isAdminActive(adminNoPerm));
+
+ // Change the target API level to MNC. Now it can be set as DA.
+ setUpPackageManagerForAdmin(adminNoPerm, DpmMockContext.CALLER_UID, null,
+ VERSION_CODES.M);
+ dpm.setActiveAdmin(adminNoPerm, /* replace =*/ false);
+ assertTrue(dpm.isAdminActive(adminNoPerm));
+
+ // TODO Test the "load from the file" case where DA will still be loaded even without
+ // BIND_DEVICE_ADMIN and target API is N.
+ }
+
+ /**
+ * Test for:
* {@link DevicePolicyManager#removeActiveAdmin}
*/
public void testRemoveActiveAdmin_SecurityException() {
@@ -458,6 +492,7 @@
*/
public void testSetDeviceOwner() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_USERS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
@@ -467,12 +502,29 @@
// Make sure admin1 is installed on system user.
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+ // Check various get APIs.
+ checkGetDeviceOwnerInfoApi(dpm, /* hasDeviceOwner =*/ false);
+
// DO needs to be an DA.
dpm.setActiveAdmin(admin1, /* replace =*/ false);
// Fire!
assertTrue(dpm.setDeviceOwner(admin1, "owner-name"));
+ // getDeviceOwnerComponent should return the admin1 component.
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnCallingUser());
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser());
+
+ // Check various get APIs.
+ checkGetDeviceOwnerInfoApi(dpm, /* hasDeviceOwner =*/ true);
+
+ // getDeviceOwnerComponent should *NOT* return the admin1 component for other users.
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser());
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser());
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
// Verify internal calls.
verify(mContext.iactivityManager, times(1)).updateDeviceOwner(
eq(admin1.getPackageName()));
@@ -485,7 +537,7 @@
MockUtils.checkIntentAction(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED),
MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
- assertEquals(admin1.getPackageName(), dpm.getDeviceOwner());
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser());
// Try to set a profile owner on the same user, which should fail.
setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_SYSTEM_USER_UID);
@@ -502,11 +554,163 @@
// DPMS.getApplicationLabel() because Context.createPackageContextAsUser() is not mockable.
}
+ private void checkGetDeviceOwnerInfoApi(DevicePolicyManager dpm, boolean hasDeviceOwner) {
+ final int origCallingUser = mContext.binder.callingUid;
+ final List origPermissions = new ArrayList(mContext.callerPermissions);
+ mContext.callerPermissions.clear();
+
+ mContext.callerPermissions.add(permission.MANAGE_USERS);
+
+ mContext.binder.callingUid = Process.SYSTEM_UID;
+
+ // TODO Test getDeviceOwnerName() too. To do so, we need to change
+ // DPMS.getApplicationLabel() because Context.createPackageContextAsUser() is not mockable.
+ if (hasDeviceOwner) {
+ assertTrue(dpm.isDeviceOwnerApp(admin1.getPackageName()));
+ assertTrue(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName()));
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnCallingUser());
+
+ assertTrue(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName()));
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser());
+ assertEquals(UserHandle.USER_SYSTEM, dpm.getDeviceOwnerUserId());
+ } else {
+ assertFalse(dpm.isDeviceOwnerApp(admin1.getPackageName()));
+ assertFalse(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName()));
+ assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser());
+
+ assertFalse(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName()));
+ assertEquals(null, dpm.getDeviceOwnerComponentOnAnyUser());
+ assertEquals(UserHandle.USER_NULL, dpm.getDeviceOwnerUserId());
+ }
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ if (hasDeviceOwner) {
+ assertTrue(dpm.isDeviceOwnerApp(admin1.getPackageName()));
+ assertTrue(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName()));
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnCallingUser());
+
+ assertTrue(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName()));
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser());
+ assertEquals(UserHandle.USER_SYSTEM, dpm.getDeviceOwnerUserId());
+ } else {
+ assertFalse(dpm.isDeviceOwnerApp(admin1.getPackageName()));
+ assertFalse(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName()));
+ assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser());
+
+ assertFalse(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName()));
+ assertEquals(null, dpm.getDeviceOwnerComponentOnAnyUser());
+ assertEquals(UserHandle.USER_NULL, dpm.getDeviceOwnerUserId());
+ }
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ // Still with MANAGE_USERS.
+ assertFalse(dpm.isDeviceOwnerApp(admin1.getPackageName()));
+ assertFalse(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName()));
+ assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser());
+
+ if (hasDeviceOwner) {
+ assertTrue(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName()));
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser());
+ assertEquals(UserHandle.USER_SYSTEM, dpm.getDeviceOwnerUserId());
+ } else {
+ assertFalse(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName()));
+ assertEquals(null, dpm.getDeviceOwnerComponentOnAnyUser());
+ assertEquals(UserHandle.USER_NULL, dpm.getDeviceOwnerUserId());
+ }
+
+ mContext.binder.callingUid = Process.SYSTEM_UID;
+ mContext.callerPermissions.remove(permission.MANAGE_USERS);
+ // System can still call "OnAnyUser" without MANAGE_USERS.
+ if (hasDeviceOwner) {
+ assertTrue(dpm.isDeviceOwnerApp(admin1.getPackageName()));
+ assertTrue(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName()));
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnCallingUser());
+
+ assertTrue(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName()));
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser());
+ assertEquals(UserHandle.USER_SYSTEM, dpm.getDeviceOwnerUserId());
+ } else {
+ assertFalse(dpm.isDeviceOwnerApp(admin1.getPackageName()));
+ assertFalse(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName()));
+ assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser());
+
+ assertFalse(dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName()));
+ assertEquals(null, dpm.getDeviceOwnerComponentOnAnyUser());
+ assertEquals(UserHandle.USER_NULL, dpm.getDeviceOwnerUserId());
+ }
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ // Still no MANAGE_USERS.
+ if (hasDeviceOwner) {
+ assertTrue(dpm.isDeviceOwnerApp(admin1.getPackageName()));
+ assertTrue(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName()));
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnCallingUser());
+ } else {
+ assertFalse(dpm.isDeviceOwnerApp(admin1.getPackageName()));
+ assertFalse(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName()));
+ assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser());
+ }
+
+ try {
+ dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName());
+ fail();
+ } catch (SecurityException expected) {
+ }
+ try {
+ dpm.getDeviceOwnerComponentOnAnyUser();
+ fail();
+ } catch (SecurityException expected) {
+ }
+ try {
+ dpm.getDeviceOwnerUserId();
+ fail();
+ } catch (SecurityException expected) {
+ }
+ try {
+ dpm.getDeviceOwnerNameOnAnyUser();
+ fail();
+ } catch (SecurityException expected) {
+ }
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ // Still no MANAGE_USERS.
+ assertFalse(dpm.isDeviceOwnerApp(admin1.getPackageName()));
+ assertFalse(dpm.isDeviceOwnerAppOnCallingUser(admin1.getPackageName()));
+ assertEquals(null, dpm.getDeviceOwnerComponentOnCallingUser());
+
+ try {
+ dpm.isDeviceOwnerAppOnAnyUser(admin1.getPackageName());
+ fail();
+ } catch (SecurityException expected) {
+ }
+ try {
+ dpm.getDeviceOwnerComponentOnAnyUser();
+ fail();
+ } catch (SecurityException expected) {
+ }
+ try {
+ dpm.getDeviceOwnerUserId();
+ fail();
+ } catch (SecurityException expected) {
+ }
+ try {
+ dpm.getDeviceOwnerNameOnAnyUser();
+ fail();
+ } catch (SecurityException expected) {
+ }
+
+ // Restore.
+ mContext.binder.callingUid = origCallingUser;
+ mContext.callerPermissions.addAll(origPermissions);
+ }
+
+
/**
* Test for: {@link DevicePolicyManager#setDeviceOwner} Package doesn't exist.
*/
public void testSetDeviceOwner_noSuchPackage() {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_USERS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
@@ -528,6 +732,7 @@
public void testClearDeviceOwner() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_USERS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
@@ -547,7 +752,7 @@
verify(mContext.iactivityManager, times(1)).updateDeviceOwner(
eq(admin1.getPackageName()));
- assertEquals(admin1.getPackageName(), dpm.getDeviceOwner());
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser());
// Set up other mocks.
when(mContext.userManager.getUserRestrictions()).thenReturn(new Bundle());
@@ -559,13 +764,14 @@
dpm.clearDeviceOwnerApp(admin1.getPackageName());
// Now DO shouldn't be set.
- assertNull(dpm.getDeviceOwner());
+ assertNull(dpm.getDeviceOwnerComponentOnAnyUser());
// TODO Check other calls.
}
public void testClearDeviceOwner_fromDifferentUser() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_USERS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
@@ -585,7 +791,7 @@
verify(mContext.iactivityManager, times(1)).updateDeviceOwner(
eq(admin1.getPackageName()));
- assertEquals(admin1.getPackageName(), dpm.getDeviceOwner());
+ assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser());
// Now call clear from the secondary user, which should throw.
mContext.binder.callingUid = DpmMockContext.CALLER_UID;
@@ -601,8 +807,8 @@
assertEquals("clearDeviceOwner can only be called by the device owner", e.getMessage());
}
- // Now DO shouldn't be set.
- assertNotNull(dpm.getDeviceOwner());
+ // DO shouldn't be removed.
+ assertTrue(dpm.isDeviceManaged());
}
public void testSetProfileOwner() throws Exception {
@@ -639,6 +845,7 @@
mMockContext.addUser(ANOTHER_USER_ID, 0); // Add one more user.
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_USERS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
@@ -667,8 +874,7 @@
mContext.setUserRunning(DpmMockContext.CALLER_USER_HANDLE, true);
assertTrue(dpm.setDeviceOwner(admin2, "owner-name", DpmMockContext.CALLER_USER_HANDLE));
- // Make sure it's set.
- assertEquals(admin2, dpm.getDeviceOwnerComponent());
+ assertEquals(admin2, dpms.getDeviceOwnerComponent(/* callingUserOnly =*/ false));
// Then check getDeviceOwnerAdminLocked().
assertEquals(admin2, dpms.getDeviceOwnerAdminLocked().info.getComponent());
@@ -692,7 +898,7 @@
dpms.mOwners.writeDeviceOwner();
// Make sure the DO component name doesn't have a class name.
- assertEquals("", dpms.getDeviceOwner().getClassName());
+ assertEquals("", dpms.getDeviceOwnerComponent(/* callingUserOnly =*/ false).getClassName());
// Then create a new DPMS to have it load the settings from files.
when(mContext.userManager.getUserRestrictions(any(UserHandle.class)))
@@ -702,7 +908,7 @@
// Now the DO component name is a full name.
// *BUT* because both admin1 and admin2 belong to the same package, we think admin1 is the
// DO.
- assertEquals(admin1, dpms.getDeviceOwner());
+ assertEquals(admin1, dpms.getDeviceOwnerComponent(/* callingUserOnly =*/ false));
}
public void testSetGetApplicationRestriction() {
@@ -740,6 +946,7 @@
public void testSetUserRestriction_asDo() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_USERS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
@@ -1000,4 +1207,64 @@
// TODO Make sure restrictions are written to the file.
}
+
+ public void testGetMacAddress() throws Exception {
+ mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+ // In this test, change the caller user to "system".
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ // Make sure admin1 is installed on system user.
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+
+ // Test 1. Caller doesn't have DO or DA.
+ try {
+ dpm.getWifiMacAddress();
+ fail();
+ } catch (SecurityException e) {
+ MoreAsserts.assertContainsRegex("No active admin owned", e.getMessage());
+ }
+
+ // DO needs to be an DA.
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+ assertTrue(dpm.isAdminActive(admin1));
+
+ // Test 2. Caller has DA, but not DO.
+ try {
+ dpm.getWifiMacAddress();
+ fail();
+ } catch (SecurityException e) {
+ MoreAsserts.assertContainsRegex("No active admin owned", e.getMessage());
+ }
+
+ // Test 3. Caller has PO, but not DO.
+ assertTrue(dpm.setProfileOwner(admin1, null, UserHandle.USER_SYSTEM));
+ try {
+ dpm.getWifiMacAddress();
+ fail();
+ } catch (SecurityException e) {
+ MoreAsserts.assertContainsRegex("No active admin owned", e.getMessage());
+ }
+
+ // Remove PO.
+ dpm.clearProfileOwner(admin1);
+
+ // Test 4, Caller is DO now.
+ assertTrue(dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM));
+
+ // 4-1. But no WifiInfo.
+ assertNull(dpm.getWifiMacAddress());
+
+ // 4-2. Returns WifiInfo, but with the default MAC.
+ when(mContext.wifiManager.getConnectionInfo()).thenReturn(new WifiInfo());
+ assertNull(dpm.getWifiMacAddress());
+
+ // 4-3. With a real MAC address.
+ final WifiInfo wi = new WifiInfo();
+ wi.setMacAddress("11:22:33:44:55:66");
+ when(mContext.wifiManager.getConnectionInfo()).thenReturn(wi);
+ assertEquals("11:22:33:44:55:66", dpm.getWifiMacAddress());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index bb1e06d..66d701d 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -31,6 +31,7 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
import android.media.IAudioService;
+import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager.WakeLock;
@@ -217,6 +218,7 @@
public final IBackupManager ibackupManager;
public final IAudioService iaudioService;
public final LockPatternUtils lockPatternUtils;
+ public final WifiManager wifiManager;
public final SettingsForMock settings;
public final MockContentResolver contentResolver;
@@ -249,6 +251,7 @@
ibackupManager = mock(IBackupManager.class);
iaudioService = mock(IAudioService.class);
lockPatternUtils = mock(LockPatternUtils.class);
+ wifiManager = mock(WifiManager.class);
settings = mock(SettingsForMock.class);
// Package manager is huge, so we use a partial mock instead.
@@ -303,6 +306,8 @@
return userManager;
case Context.POWER_SERVICE:
return powerManager;
+ case Context.WIFI_SERVICE:
+ return wifiManager;
}
throw new UnsupportedOperationException();
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index e11f3fb..5b33e4d 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -43,6 +43,7 @@
public ComponentName admin1;
public ComponentName admin2;
public ComponentName admin3;
+ public ComponentName adminNoPerm;
@Override
protected void setUp() throws Exception {
@@ -56,6 +57,7 @@
admin1 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin1.class);
admin2 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin2.class);
admin3 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin3.class);
+ adminNoPerm = new ComponentName(mRealTestContext, DummyDeviceAdmins.AdminNoPerm.class);
}
@Override
@@ -67,11 +69,36 @@
protected void setUpPackageManagerForAdmin(ComponentName admin, int packageUid)
throws Exception {
setUpPackageManagerForAdmin(admin, packageUid,
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
+ /* enabledSetting =*/ null, /* appTargetSdk = */ null);
}
protected void setUpPackageManagerForAdmin(ComponentName admin, int packageUid,
int enabledSetting) throws Exception {
+ setUpPackageManagerForAdmin(admin, packageUid, enabledSetting, /* appTargetSdk = */ null);
+ }
+
+ protected void setUpPackageManagerForAdmin(ComponentName admin, int packageUid,
+ Integer enabledSetting, Integer appTargetSdk) throws Exception {
+
+ // Set up getApplicationInfo().
+
+ final ApplicationInfo ai = DpmTestUtils.cloneParcelable(
+ mRealTestContext.getPackageManager().getApplicationInfo(
+ admin.getPackageName(),
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS));
+
+ ai.enabledSetting = enabledSetting == null
+ ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
+ : enabledSetting;
+ if (appTargetSdk != null) {
+ ai.targetSdkVersion = appTargetSdk;
+ }
+ ai.uid = packageUid;
+
+ doReturn(ai).when(mMockContext.ipackageManager).getApplicationInfo(
+ eq(admin.getPackageName()),
+ eq(PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
+ eq(UserHandle.getUserId(packageUid)));
// Set up queryBroadcastReceivers().
@@ -88,7 +115,7 @@
realResolveInfo.set(0, DpmTestUtils.cloneParcelable(realResolveInfo.get(0)));
// We need to rewrite the UID in the activity info.
- realResolveInfo.get(0).activityInfo.applicationInfo.uid = packageUid;
+ realResolveInfo.get(0).activityInfo.applicationInfo = ai;
doReturn(realResolveInfo).when(mMockContext.packageManager).queryBroadcastReceivers(
MockUtils.checkIntentComponent(admin),
@@ -96,21 +123,6 @@
| PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
eq(UserHandle.getUserId(packageUid)));
- // Set up getApplicationInfo().
-
- final ApplicationInfo ai = DpmTestUtils.cloneParcelable(
- mRealTestContext.getPackageManager().getApplicationInfo(
- admin.getPackageName(),
- PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS));
-
- ai.enabledSetting = enabledSetting;
- ai.uid = packageUid;
-
- doReturn(ai).when(mMockContext.ipackageManager).getApplicationInfo(
- eq(admin.getPackageName()),
- eq(PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
- eq(UserHandle.getUserId(packageUid)));
-
// Set up getPackageInfo().
final PackageInfo pi = DpmTestUtils.cloneParcelable(
@@ -118,7 +130,7 @@
admin.getPackageName(), 0));
assertTrue(pi.applicationInfo.flags != 0);
- pi.applicationInfo.uid = packageUid;
+ pi.applicationInfo = ai;
doReturn(pi).when(mMockContext.ipackageManager).getPackageInfo(
eq(admin.getPackageName()),
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DummyDeviceAdmins.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DummyDeviceAdmins.java
index 08293a2..a0f4d97 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DummyDeviceAdmins.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DummyDeviceAdmins.java
@@ -24,4 +24,6 @@
}
public static class Admin3 extends DeviceAdminReceiver {
}
+ public static class AdminNoPerm extends DeviceAdminReceiver {
+ }
}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 8b347cc..b5b4e5f 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1051,7 +1051,9 @@
* If there is a ringing incoming call, this method accepts the call on behalf of the user.
* TODO: L-release - need to convert all invocation of ITelecmmService#answerRingingCall to use
* this method (clockwork & gearhead).
- *
+ * If the incoming call is a video call, the call will be answered with the same video state as
+ * the incoming call requests. This means, for example, that an incoming call requesting
+ * {@link VideoProfile#STATE_BIDIRECTIONAL} will be answered, accepting that state.
* @hide
*/
@SystemApi
@@ -1066,6 +1068,24 @@
}
/**
+ * If there is a ringing incoming call, this method accepts the call on behalf of the user,
+ * with the specified video state.
+ *
+ * @param videoState The desired video state to answer the call with.
+ * @hide
+ */
+ @SystemApi
+ public void acceptRingingCall(int videoState) {
+ try {
+ if (isServiceConnected()) {
+ getTelecomService().acceptRingingCallWithVideoState(videoState);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#acceptRingingCallWithVideoState", e);
+ }
+ }
+
+ /**
* Silences the ringer if a ringing call exists.
*
* Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 2e07759..856e210 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -178,6 +178,11 @@
void acceptRingingCall();
/**
+ * @see TelecomServiceImpl#acceptRingingCallWithVideoState(int)
+ */
+ void acceptRingingCallWithVideoState(int videoState);
+
+ /**
* @see TelecomServiceImpl#cancelMissedCallsNotification
*/
void cancelMissedCallsNotification(String callingPackage);
diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
index ef39a6c..4785169 100644
--- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
@@ -1012,7 +1012,7 @@
*
* @param tables the new list of enabled single shift tables
*/
- static synchronized void setEnabledSingleShiftTables(int[] tables) {
+ public static synchronized void setEnabledSingleShiftTables(int[] tables) {
sEnabledSingleShiftTables = tables;
sDisableCountryEncodingCheck = true;
@@ -1030,7 +1030,7 @@
*
* @param tables the new list of enabled locking shift tables
*/
- static synchronized void setEnabledLockingShiftTables(int[] tables) {
+ public static synchronized void setEnabledLockingShiftTables(int[] tables) {
sEnabledLockingShiftTables = tables;
sDisableCountryEncodingCheck = true;
}
@@ -1042,7 +1042,7 @@
*
* @return the list of enabled single shift tables
*/
- static synchronized int[] getEnabledSingleShiftTables() {
+ public static synchronized int[] getEnabledSingleShiftTables() {
return sEnabledSingleShiftTables;
}
@@ -1053,7 +1053,7 @@
*
* @return the list of enabled locking shift tables
*/
- static synchronized int[] getEnabledLockingShiftTables() {
+ public static synchronized int[] getEnabledLockingShiftTables() {
return sEnabledLockingShiftTables;
}
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index e25b38c..9f8af6e 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -424,6 +424,15 @@
return mMacAddress;
}
+ /**
+ * @return true if {@link #getMacAddress()} has a real MAC address.
+ *
+ * @hide
+ */
+ public boolean hasRealMacAddress() {
+ return mMacAddress != null && !DEFAULT_MAC_ADDRESS.equals(mMacAddress);
+ }
+
/** {@hide} */
public void setMeteredHint(boolean meteredHint) {
mMeteredHint = meteredHint;