Merge "Don't trigger click if long press already happened"
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/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 e758835..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
@@ -973,7 +978,7 @@
if (newFragment != null) {
transaction.add(R.id.embedded_content_container, newFragment, FRAGMENT_TAG);
}
- transaction.commit();
+ transaction.commitAllowingStateLoss();
getFragmentManager().executePendingTransactions();
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 3971706..fbf8a2bd9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -54,7 +54,6 @@
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
@@ -70,7 +69,6 @@
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
@@ -148,22 +146,6 @@
private static final Bundle NULL_SETTING = Bundle.forPair(Settings.NameValueTable.VALUE, null);
- // Per user settings that cannot be modified if associated user restrictions are enabled.
- private static final Map<String, String> sSettingToUserRestrictionMap = new ArrayMap<>();
- static {
- sSettingToUserRestrictionMap.put(Settings.Secure.LOCATION_MODE,
- UserManager.DISALLOW_SHARE_LOCATION);
- sSettingToUserRestrictionMap.put(Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- UserManager.DISALLOW_SHARE_LOCATION);
- sSettingToUserRestrictionMap.put(Settings.Secure.INSTALL_NON_MARKET_APPS,
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
- sSettingToUserRestrictionMap.put(Settings.Global.ADB_ENABLED,
- UserManager.DISALLOW_DEBUGGING_FEATURES);
- sSettingToUserRestrictionMap.put(Settings.Global.PACKAGE_VERIFIER_ENABLE,
- UserManager.ENSURE_VERIFY_APPS);
- sSettingToUserRestrictionMap.put(Settings.Global.PREFERRED_NETWORK_MODE,
- UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
- }
// Per user secure settings that moved to the for all users global settings.
static final Set<String> sSecureMovedToGlobalSettings = new ArraySet<>();
@@ -647,8 +629,9 @@
// Resolve the userId on whose behalf the call is made.
final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
- // If this is a setting that is currently restricted for this user, done.
- if (isGlobalOrSecureSettingRestrictedForUser(name, callingUserId)) {
+ // If this is a setting that is currently restricted for this user, do not allow
+ // unrestricting changes.
+ if (isGlobalOrSecureSettingRestrictedForUser(name, callingUserId, value)) {
return false;
}
@@ -772,8 +755,9 @@
// Resolve the userId on whose behalf the call is made.
final int callingUserId = resolveCallingUserIdEnforcingPermissionsLocked(requestingUserId);
- // If this is a setting that is currently restricted for this user, done.
- if (isGlobalOrSecureSettingRestrictedForUser(name, callingUserId)) {
+ // If this is a setting that is currently restricted for this user, do not allow
+ // unrestricting changes.
+ if (isGlobalOrSecureSettingRestrictedForUser(name, callingUserId, value)) {
return false;
}
@@ -978,12 +962,59 @@
return false;
}
- private boolean isGlobalOrSecureSettingRestrictedForUser(String setting, int userId) {
- String restriction = sSettingToUserRestrictionMap.get(setting);
- if (restriction == null) {
- return false;
+ /**
+ * Checks whether changing a setting to a value is prohibited by the corresponding user
+ * restriction.
+ *
+ * <p>See also {@link com.android.server.pm.UserRestrictionsUtils#applyUserRestrictionLR},
+ * which should be in sync with this method.
+ *
+ * @return true if the change is prohibited, false if the change is allowed.
+ */
+ private boolean isGlobalOrSecureSettingRestrictedForUser(String setting, int userId,
+ String value) {
+ String restriction;
+ switch (setting) {
+ case Settings.Secure.LOCATION_MODE:
+ // Note LOCATION_MODE will be converted into LOCATION_PROVIDERS_ALLOWED
+ // in android.provider.Settings.Secure.putStringForUser(), so we shouldn't come
+ // here normally, but we still protect it here from a direct provider write.
+ if (String.valueOf(Settings.Secure.LOCATION_MODE_OFF).equals(value)) return false;
+ restriction = UserManager.DISALLOW_SHARE_LOCATION;
+ break;
+
+ case Settings.Secure.LOCATION_PROVIDERS_ALLOWED:
+ // See SettingsProvider.updateLocationProvidersAllowedLocked. "-" is to disable
+ // a provider, which should be allowed even if the user restriction is set.
+ if (value != null && value.startsWith("-")) return false;
+ restriction = UserManager.DISALLOW_SHARE_LOCATION;
+ break;
+
+ case Settings.Secure.INSTALL_NON_MARKET_APPS:
+ if ("0".equals(value)) return false;
+ restriction = UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES;
+ break;
+
+ case Settings.Global.ADB_ENABLED:
+ if ("0".equals(value)) return false;
+ restriction = UserManager.DISALLOW_DEBUGGING_FEATURES;
+ break;
+
+ case Settings.Global.PACKAGE_VERIFIER_ENABLE:
+ case Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB:
+ if ("1".equals(value)) return false;
+ restriction = UserManager.ENSURE_VERIFY_APPS;
+ break;
+
+ case Settings.Global.PREFERRED_NETWORK_MODE:
+ restriction = UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS;
+ break;
+
+ default:
+ return false;
}
- return mUserManager.hasUserRestriction(restriction, new UserHandle(userId));
+
+ return mUserManager.hasUserRestriction(restriction, UserHandle.of(userId));
}
private int resolveOwningUserIdForSecureSettingLocked(int userId, String setting) {
@@ -1094,6 +1125,9 @@
* But helper functions in android.providers.Settings can enable or disable
* a single provider by using a "+" or "-" prefix before the provider name.
*
+ * <p>See also {@link #isGlobalOrSecureSettingRestrictedForUser()}. If DISALLOW_SHARE_LOCATION
+ * is set, the said method will only allow values with the "-" prefix.
+ *
* @returns whether the enabled location providers changed.
*/
private boolean updateLocationProvidersAllowedLocked(String value, int owningUserId) {
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/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/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/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 85453a8..77abd3e 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -285,6 +285,10 @@
* <p>Note this method is called by {@link UserManagerService} while holding
* {@code mRestrictionLock}. Be aware when calling into other services, which could cause
* a deadlock.
+ *
+ * <p>See also {@link
+ * com.android.providers.settings.SettingsProvider#isGlobalOrSecureSettingRestrictedForUser},
+ * which should be in sync with this method.
*/
private static void applyUserRestrictionLR(Context context, int userId, String key,
boolean newValue) {
@@ -306,7 +310,7 @@
case UserManager.DISALLOW_CONFIG_WIFI:
if (newValue) {
android.provider.Settings.Secure.putIntForUser(cr,
- android.provider.Settings.Secure
+ android.provider.Settings.Global
.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0, userId);
}
break;
@@ -316,9 +320,6 @@
android.provider.Settings.Secure.LOCATION_MODE,
android.provider.Settings.Secure.LOCATION_MODE_OFF,
userId);
- android.provider.Settings.Secure.putStringForUser(cr,
- android.provider.Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "",
- userId);
}
// Send out notifications as some clients may want to reread the
// value which actually changed due to a restriction having been
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;