diff options
| author | 2010-08-03 10:51:06 -0700 | |
|---|---|---|
| committer | 2010-08-04 16:01:56 -0700 | |
| commit | 69238c6a37ae43c748ad9cd7613f2209149ee7da (patch) | |
| tree | fc77df6662f66584d96d4c035a4ef719f9d87934 | |
| parent | a69fbc8effd5aaf7a5eeafed0c0fc69bcd3e7e15 (diff) | |
Device Policy Manager changes to enable Global Proxy.
Change-Id: I8489df7d28e4c5312e10d5cecc8e2a182413a034
| -rw-r--r-- | api/current.xml | 28 | ||||
| -rw-r--r-- | core/java/android/app/admin/DeviceAdminInfo.java | 15 | ||||
| -rw-r--r-- | core/java/android/app/admin/DevicePolicyManager.java | 90 | ||||
| -rw-r--r-- | core/java/android/app/admin/IDevicePolicyManager.aidl | 3 | ||||
| -rw-r--r-- | core/res/res/values/strings.xml | 5 | ||||
| -rw-r--r-- | services/java/com/android/server/DevicePolicyManagerService.java | 137 |
6 files changed, 277 insertions, 1 deletions
diff --git a/api/current.xml b/api/current.xml index d87acf941b8d..5b17c2bfcf01 100644 --- a/api/current.xml +++ b/api/current.xml @@ -34339,6 +34339,17 @@ visibility="public" > </method> +<method name="getGlobalProxyAdmin" + return="android.content.ComponentName" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getMaximumFailedPasswordsForWipe" return="int" abstract="false" @@ -34558,6 +34569,23 @@ <parameter name="flags" type="int"> </parameter> </method> +<method name="setGlobalProxy" + return="android.content.ComponentName" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="admin" type="android.content.ComponentName"> +</parameter> +<parameter name="proxySpec" type="java.net.Proxy"> +</parameter> +<parameter name="exclusionList" type="java.util.List<java.lang.String>"> +</parameter> +</method> <method name="setMaximumFailedPasswordsForWipe" return="void" abstract="false" diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java index 0bcd65cd48fe..e09cda26674e 100644 --- a/core/java/android/app/admin/DeviceAdminInfo.java +++ b/core/java/android/app/admin/DeviceAdminInfo.java @@ -103,6 +103,16 @@ public final class DeviceAdminInfo implements Parcelable { */ public static final int USES_POLICY_WIPE_DATA = 4; + /** + * A type of policy that this device admin can use: able to specify the + * device Global Proxy, via {@link DevicePolicyManager#setGlobalProxy}. + * + * <p>To control this policy, the device admin must have a "set-global-proxy" + * tag in the "uses-policies" section of its meta-data. + * @hide + */ + public static final int USES_POLICY_SETS_GLOBAL_PROXY = 5; + /** @hide */ public static class PolicyInfo { public final int ident; @@ -138,6 +148,9 @@ public final class DeviceAdminInfo implements Parcelable { sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_FORCE_LOCK, "force-lock", com.android.internal.R.string.policylab_forceLock, com.android.internal.R.string.policydesc_forceLock)); + sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_SETS_GLOBAL_PROXY, "set-global-proxy", + com.android.internal.R.string.policylab_setGlobalProxy, + com.android.internal.R.string.policydesc_setGlobalProxy)); for (int i=0; i<sPoliciesDisplayOrder.size(); i++) { PolicyInfo pi = sPoliciesDisplayOrder.get(i); @@ -328,7 +341,7 @@ public final class DeviceAdminInfo implements Parcelable { * the given policy control. The possible policy identifier inputs are: * {@link #USES_POLICY_LIMIT_PASSWORD}, {@link #USES_POLICY_WATCH_LOGIN}, * {@link #USES_POLICY_RESET_PASSWORD}, {@link #USES_POLICY_FORCE_LOCK}, - * {@link #USES_POLICY_WIPE_DATA}. + * {@link #USES_POLICY_WIPE_DATA}, {@link #USES_POLICY_SETS_GLOBAL_PROXY}. */ public boolean usesPolicy(int policyIdent) { return (mUsesPolicies & (1<<policyIdent)) != 0; diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 3066f5ca47c9..1255e63c2a21 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -32,6 +32,8 @@ import android.os.ServiceManager; import android.util.Log; import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Proxy; import java.util.List; /** @@ -915,6 +917,93 @@ public class DevicePolicyManager { } /** + * Called by an application that is administering the device to set the + * global proxy and exclusion list. + * <p> + * The calling device admin must have requested + * {@link DeviceAdminInfo#USES_POLICY_SETS_GLOBAL_PROXY} to be able to call + * this method; if it has not, a security exception will be thrown. + * Only the first device admin can set the proxy. If a second admin attempts + * to set the proxy, the {@link ComponentName} of the admin originally setting the + * proxy will be returned. If successful in setting the proxy, null will + * be returned. + * The method can be called repeatedly by the device admin alrady setting the + * proxy to update the proxy and exclusion list. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated + * with. + * @param proxySpec the global proxy desired. Must be an HTTP Proxy. + * Pass Proxy.NO_PROXY to reset the proxy. + * @param exclusionList a list of domains to be excluded from the global proxy. + * @param proxyAdmins an empty, mutable list that will contain any proxy admins + * that define a global proxy. + * @return returns null if the proxy was successfully set, or a {@link ComponentName} + * of the device admin that sets thew proxy otherwise. + */ + public ComponentName setGlobalProxy(ComponentName admin, Proxy proxySpec, + List<String> exclusionList ) { + if (proxySpec == null) { + throw new NullPointerException(); + } + if (mService != null) { + try { + String hostSpec; + String exclSpec; + if (proxySpec.equals(Proxy.NO_PROXY)) { + hostSpec = null; + exclSpec = null; + } else { + if (!proxySpec.type().equals(Proxy.Type.HTTP)) { + throw new IllegalArgumentException(); + } + InetSocketAddress sa = (InetSocketAddress)proxySpec.address(); + String hostName = sa.getHostName(); + int port = sa.getPort(); + StringBuilder hostBuilder = new StringBuilder(); + hostSpec = hostBuilder.append(hostName) + .append(":").append(Integer.toString(port)).toString(); + if (exclusionList == null) { + exclSpec = ""; + } else { + StringBuilder listBuilder = new StringBuilder(); + boolean firstDomain = true; + for (String exclDomain : exclusionList) { + if (!firstDomain) { + listBuilder = listBuilder.append(","); + } else { + firstDomain = false; + } + listBuilder = listBuilder.append(exclDomain.trim()); + } + exclSpec = listBuilder.toString(); + } + android.net.Proxy.validate(hostName, Integer.toString(port), exclSpec); + } + return mService.setGlobalProxy(admin, hostSpec, exclSpec); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + return null; + } + + /** + * Returns the component name setting the global proxy. + * @return ComponentName object of the device admin that set the global proxy, or + * null if no admin has set the proxy. + */ + public ComponentName getGlobalProxyAdmin() { + if (mService != null) { + try { + return mService.getGlobalProxyAdmin(); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + return null; + } + + /** * @hide */ public void setActiveAdmin(ComponentName policyReceiver) { @@ -1007,4 +1096,5 @@ public class DevicePolicyManager { } } } + } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 3ada95cf6264..3fcd6fce5140 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -66,6 +66,9 @@ interface IDevicePolicyManager { void lockNow(); void wipeData(int flags); + + ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList); + ComponentName getGlobalProxyAdmin(); void setActiveAdmin(in ComponentName policyReceiver); boolean isAdminActive(in ComponentName policyReceiver); diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 0304b1dabe63..32a1f0dbed2e 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1252,6 +1252,11 @@ <!-- Description of policy access to wipe the user's data --> <string name="policydesc_wipeData">Perform a factory reset, deleting all of your data without any confirmation.</string> + <string name="policylab_setGlobalProxy">Set the device global proxy</string> + <!-- Description of policy access to wipe the user's data --> + <string name="policydesc_setGlobalProxy">Set the device global proxy + to be used while policy is enabled. Only the first device admin + sets the effective global proxy.</string> <!-- The order of these is important, don't reorder without changing Contacts.java --> <skip /> <!-- Phone number types from android.provider.Contacts. This could be used when adding a new phone number for a contact, for example. --> diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index c0ea68d73190..0c3a0e6dde5e 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -33,6 +33,7 @@ import android.app.admin.DevicePolicyManager; import android.app.admin.IDevicePolicyManager; import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -46,6 +47,8 @@ import android.os.RemoteCallback; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; +import android.net.Proxy; +import android.provider.Settings; import android.util.Slog; import android.util.PrintWriterPrinter; import android.util.Printer; @@ -58,9 +61,11 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; +import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Set; /** * Implementation of the device policy APIs. @@ -105,6 +110,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { long maximumTimeToUnlock = 0; int maximumFailedPasswordsForWipe = 0; + // TODO: review implementation decisions with frameworks team + boolean specifiesGlobalProxy = false; + String globalProxySpec = null; + String globalProxyExclusionList = null; + ActiveAdmin(DeviceAdminInfo _info) { info = _info; } @@ -171,6 +181,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { out.attribute(null, "value", Integer.toString(maximumFailedPasswordsForWipe)); out.endTag(null, "max-failed-password-wipe"); } + if (specifiesGlobalProxy) { + out.startTag(null, "specifies-global-proxy"); + out.attribute(null, "value", Boolean.toString(specifiesGlobalProxy)); + out.endTag(null, "specifies_global_proxy"); + if (globalProxySpec != null) { + out.startTag(null, "global-proxy-spec"); + out.attribute(null, "value", globalProxySpec); + out.endTag(null, "global-proxy-spec"); + } + if (globalProxyExclusionList != null) { + out.startTag(null, "global-proxy-exclusion-list"); + out.attribute(null, "value", globalProxyExclusionList); + out.endTag(null, "global-proxy-exclusion-list"); + } + } } void readFromXml(XmlPullParser parser) @@ -218,6 +243,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } else if ("max-failed-password-wipe".equals(tag)) { maximumFailedPasswordsForWipe = Integer.parseInt( parser.getAttributeValue(null, "value")); + } else if ("specifies-global-proxy".equals(tag)) { + specifiesGlobalProxy = Boolean.getBoolean( + parser.getAttributeValue(null, "value")); + } else if ("global-proxy-spec".equals(tag)) { + globalProxySpec = + parser.getAttributeValue(null, "value"); + } else if ("global-proxy-exclusion-list".equals(tag)) { + globalProxyExclusionList = + parser.getAttributeValue(null, "value"); } else { Slog.w(TAG, "Unknown admin tag: " + tag); } @@ -256,6 +290,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { pw.println(maximumTimeToUnlock); pw.print(prefix); pw.print("maximumFailedPasswordsForWipe="); pw.println(maximumFailedPasswordsForWipe); + pw.print(prefix); pw.print("specifiesGlobalProxy="); + pw.println(specifiesGlobalProxy); + if (globalProxySpec != null) { + pw.print(prefix); pw.print("globalProxySpec="); + pw.println(globalProxySpec); + } + if (globalProxyExclusionList != null) { + pw.print(prefix); pw.print("globalProxyEclusionList="); + pw.println(globalProxyExclusionList); + } } } @@ -370,12 +414,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { void removeActiveAdminLocked(ComponentName adminReceiver) { ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver); if (admin != null) { + boolean doProxyCleanup = + admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY); sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED); // XXX need to wait for it to complete. mAdminList.remove(admin); mAdminMap.remove(adminReceiver); validatePasswordOwnerLocked(); + if (doProxyCleanup) { + resetGlobalProxy(); + } } } @@ -1417,6 +1466,94 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + public ComponentName setGlobalProxy(ComponentName who, String proxySpec, + String exclusionList) { + synchronized(this) { + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + + ActiveAdmin admin = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY); + + // Scan through active admins and find if anyone has already + // set the global proxy. + final int N = mAdminList.size(); + Set<ComponentName> compSet = mAdminMap.keySet(); + for (ComponentName component : compSet) { + ActiveAdmin ap = mAdminMap.get(component); + if ((ap.specifiesGlobalProxy) && (!component.equals(who))) { + // Another admin already sets the global proxy + // Return it to the caller. + return component; + } + } + if (proxySpec == null) { + admin.specifiesGlobalProxy = false; + admin.globalProxySpec = null; + admin.globalProxyExclusionList = null; + } else { + + admin.specifiesGlobalProxy = true; + admin.globalProxySpec = proxySpec; + admin.globalProxyExclusionList = exclusionList; + } + + // Reset the global proxy accordingly + // Do this using system permissions, as apps cannot write to secure settings + long origId = Binder.clearCallingIdentity(); + resetGlobalProxy(); + Binder.restoreCallingIdentity(origId); + return null; + } + } + + public ComponentName getGlobalProxyAdmin() { + synchronized(this) { + // Scan through active admins and find if anyone has already + // set the global proxy. + final int N = mAdminList.size(); + for (int i = 0; i < N; i++) { + ActiveAdmin ap = mAdminList.get(i); + if (ap.specifiesGlobalProxy) { + // Device admin sets the global proxy + // Return it to the caller. + return ap.info.getComponent(); + } + } + } + // No device admin sets the global proxy. + return null; + } + + private void resetGlobalProxy() { + final int N = mAdminList.size(); + for (int i = 0; i < N; i++) { + ActiveAdmin ap = mAdminList.get(i); + if (ap.specifiesGlobalProxy) { + saveGlobalProxy(ap.globalProxySpec, ap.globalProxyExclusionList); + return; + } + } + // No device admins defining global proxies - reset global proxy settings to none + saveGlobalProxy(null, null); + } + + private void saveGlobalProxy(String proxySpec, String exclusionList) { + if (exclusionList == null) { + exclusionList = ""; + } + if (proxySpec == null) { + proxySpec = ""; + } + // Remove white spaces + proxySpec = proxySpec.trim(); + exclusionList = exclusionList.trim(); + ContentResolver res = mContext.getContentResolver(); + Settings.Secure.putString(res, Settings.Secure.HTTP_PROXY, proxySpec); + Settings.Secure.putString(res, Settings.Secure.HTTP_PROXY_EXCLUSION_LIST, exclusionList); + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) |