Move DeviceAdmin APIs to android.app.admin.

Also add ability for admins to hide themselves when not in use,
a facility for admins to not allow other admins to reset
their password, and debug dumping.
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index 9899e99..a555244 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -26,10 +26,10 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import android.app.Activity;
-import android.app.DeviceAdminReceiver;
-import android.app.DeviceAdminInfo;
-import android.app.DevicePolicyManager;
-import android.app.IDevicePolicyManager;
+import android.app.admin.DeviceAdminInfo;
+import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.IDevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -46,13 +46,17 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.util.Log;
+import android.util.PrintWriterPrinter;
+import android.util.Printer;
 import android.util.Xml;
 import android.view.WindowManagerPolicy;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -72,6 +76,8 @@
     int mActivePasswordLength = 0;
     int mFailedPasswordAttempts = 0;
     
+    int mPasswordOwner = -1;
+    
     final HashMap<ComponentName, ActiveAdmin> mAdminMap
             = new HashMap<ComponentName, ActiveAdmin>();
     final ArrayList<ActiveAdmin> mAdminList
@@ -148,11 +154,31 @@
                 XmlUtils.skipCurrentTag(parser);
             }
         }
+        
+        void dump(String prefix, PrintWriter pw) {
+            pw.print(prefix); pw.print("uid="); pw.println(getUid());
+            pw.print(prefix); pw.println("policies:");
+            ArrayList<DeviceAdminInfo.PolicyInfo> pols = info.getUsedPolicies();
+            if (pols != null) {
+                for (int i=0; i<pols.size(); i++) {
+                    pw.print(prefix); pw.print("  "); pw.println(pols.get(i).tag);
+                }
+            }
+            pw.print(prefix); pw.print("passwordQuality=");
+                    pw.print(passwordQuality);
+                    pw.print(" minimumPasswordLength=");
+                    pw.println(minimumPasswordLength);
+            pw.print(prefix); pw.print("maximumTimeToUnlock=");
+                    pw.println(maximumTimeToUnlock);
+            pw.print(prefix); pw.print("maximumFailedPasswordsForWipe=");
+                    pw.println(maximumFailedPasswordsForWipe);
+        }
     }
     
     class MyPackageMonitor extends PackageMonitor {
         public void onSomePackagesChanged() {
             synchronized (DevicePolicyManagerService.this) {
+                boolean removed = false;
                 for (int i=mAdminList.size()-1; i>=0; i--) {
                     ActiveAdmin aa = mAdminList.get(i);
                     int change = isPackageDisappearing(aa.info.getPackageName()); 
@@ -160,6 +186,7 @@
                             || change == PACKAGE_TEMPORARY_CHANGE) {
                         Log.w(TAG, "Admin unexpectedly uninstalled: "
                                 + aa.info.getComponent());
+                        removed = true;
                         mAdminList.remove(i);
                     } else if (isPackageModified(aa.info.getPackageName())) {
                         try {
@@ -168,10 +195,14 @@
                         } catch (NameNotFoundException e) {
                             Log.w(TAG, "Admin package change removed component: "
                                     + aa.info.getComponent());
+                            removed = true;
                             mAdminList.remove(i);
                         }
                     }
                 }
+                if (removed) {
+                    validatePasswordOwnerLocked();
+                }
             }
         }
     }
@@ -260,6 +291,7 @@
             // XXX need to wait for it to complete.
             mAdminList.remove(admin);
             mAdminMap.remove(adminReceiver);
+            validatePasswordOwnerLocked();
         }
     }
     
@@ -312,6 +344,12 @@
             
             out.endTag(null, "policies");
 
+            if (mPasswordOwner >= 0) {
+                out.startTag(null, "password-owner");
+                out.attribute(null, "value", Integer.toString(mPasswordOwner));
+                out.endTag(null, "password-owner");
+            }
+            
             if (mFailedPasswordAttempts != 0) {
                 out.startTag(null, "failed-password-attempts");
                 out.attribute(null, "value", Integer.toString(mFailedPasswordAttempts));
@@ -377,6 +415,10 @@
                     mFailedPasswordAttempts = Integer.parseInt(
                             parser.getAttributeValue(null, "value"));
                     XmlUtils.skipCurrentTag(parser);
+                } else if ("password-owner".equals(tag)) {
+                    mPasswordOwner = Integer.parseInt(
+                            parser.getAttributeValue(null, "value"));
+                    XmlUtils.skipCurrentTag(parser);
                 } else {
                     Log.w(TAG, "Unknown tag: " + tag);
                     XmlUtils.skipCurrentTag(parser);
@@ -401,6 +443,8 @@
             // Ignore
         }
 
+        validatePasswordOwnerLocked();
+        
         long timeMs = getMaximumTimeToLock(null);
         if (timeMs <= 0) {
             timeMs = Integer.MAX_VALUE;
@@ -412,6 +456,23 @@
         }
     }
 
+    void validatePasswordOwnerLocked() {
+        if (mPasswordOwner >= 0) {
+            boolean haveOwner = false;
+            for (int i=mAdminList.size()-1; i>=0; i--) {
+                if (mAdminList.get(i).getUid() == mPasswordOwner) {
+                    haveOwner = true;
+                    break;
+                }
+            }
+            if (!haveOwner) {
+                Log.w(TAG, "Previous password owner " + mPasswordOwner
+                        + " no longer active; disabling");
+                mPasswordOwner = -1;
+            }
+        }
+    }
+    
     public void systemReady() {
         synchronized (this) {
             loadSettingsLocked();
@@ -622,7 +683,7 @@
         }
     }
     
-    public boolean resetPassword(String password) {
+    public boolean resetPassword(String password, int flags) {
         int quality;
         synchronized (this) {
             // This API can only be called by an active device admin,
@@ -645,12 +706,24 @@
             }
         }
         
+        int callingUid = Binder.getCallingUid();
+        if (mPasswordOwner >= 0 && mPasswordOwner != callingUid) {
+            Log.w(TAG, "resetPassword: already set by another uid and not entered by user");
+            return false;
+        }
+        
         // Don't do this with the lock held, because it is going to call
         // back in to the service.
         long ident = Binder.clearCallingIdentity();
         try {
             LockPatternUtils utils = new LockPatternUtils(mContext);
             utils.saveLockPassword(password, quality);
+            int newOwner = (flags&DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY)
+                    != 0 ? callingUid : -1;
+            if (mPasswordOwner != newOwner) {
+                mPasswordOwner = newOwner;
+                saveSettingsLocked();
+            }
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -829,10 +902,11 @@
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
         
         synchronized (this) {
-            if (mFailedPasswordAttempts != 0) {
+            if (mFailedPasswordAttempts != 0 || mPasswordOwner >= 0) {
                 long ident = Binder.clearCallingIdentity();
                 try {
                     mFailedPasswordAttempts = 0;
+                    mPasswordOwner = -1;
                     saveSettingsLocked();
                     sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_SUCCEEDED,
                             DeviceAdminInfo.USES_POLICY_WATCH_LOGIN);
@@ -842,4 +916,39 @@
             }
         }
     }
+    
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                != PackageManager.PERMISSION_GRANTED) {
+
+            pw.println("Permission Denial: can't dump DevicePolicyManagerService from from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid());
+            return;
+        }
+        
+        final Printer p = new PrintWriterPrinter(pw);
+        
+        synchronized (this) {
+            p.println("Current Device Policy Manager state:");
+            
+            p.println("  Enabled Device Admins:");
+            final int N = mAdminList.size();
+            for (int i=0; i<N; i++) {
+                ActiveAdmin ap = mAdminList.get(i);
+                if (ap != null) {
+                    pw.print("  "); pw.print(ap.info.getComponent().flattenToShortString());
+                            pw.println(":");
+                    ap.dump("    ", pw);
+                }
+            }
+            
+            pw.println(" ");
+            pw.print("  mActivePasswordQuality="); pw.println(mActivePasswordQuality);
+            pw.print("  mActivePasswordLength="); pw.println(mActivePasswordLength);
+            pw.print("  mFailedPasswordAttempts="); pw.println(mFailedPasswordAttempts);
+            pw.print("  mPasswordOwner="); pw.println(mPasswordOwner);
+        }
+    }
 }